Commit 22f1fd03 authored by Brian Williams's avatar Brian Williams

Improve feature flag usage

This change improves feature flag checks for the `default_branch_image`
feature. We now check that the feature flag is enabled in the report
parser so that we can use `@report.pipeline.project` in order to enable
or disable the feature on a per-project basis. The feature flag status
is passed to the location parser as an attribute.
parent f2824f7d
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
name: improved_container_scan_matching name: improved_container_scan_matching
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73486 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73486
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344534 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344534
milestone: '14.5' milestone: '14.6'
type: development type: development
group: group::container security group: group::container security
default_enabled: false default_enabled: false
...@@ -13,15 +13,22 @@ module Gitlab ...@@ -13,15 +13,22 @@ module Gitlab
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) default_branch_image: default_branch_image(location_data),
improved_container_scan_matching_enabled: improved_container_scan_matching_enabled?
) )
end end
def default_branch_image(location_data) def default_branch_image(location_data)
return unless improved_container_scan_matching_enabled?
return if @report.pipeline.default_branch? return if @report.pipeline.default_branch?
location_data['default_branch_image'] location_data['default_branch_image']
end end
def improved_container_scan_matching_enabled?
Feature.enabled?(:improved_container_scan_matching, @report.pipeline.project, default_enabled: :yaml)
end
end end
end end
end end
......
...@@ -6,34 +6,49 @@ module Gitlab ...@@ -6,34 +6,49 @@ module Gitlab
module Security module Security
module Locations module Locations
class ContainerScanning < Base class ContainerScanning < Base
attr_reader :default_branch_image
attr_reader :image attr_reader :image
attr_reader :operating_system attr_reader :operating_system
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, default_branch_image: nil) def initialize(
image:,
operating_system:,
package_name: nil,
package_version: nil,
default_branch_image: nil,
improved_container_scan_matching_enabled: false
)
@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 @default_branch_image = default_branch_image
@improved_container_scan_matching_enabled = improved_container_scan_matching_enabled
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 def improved_container_scan_matching_enabled?
return @default_branch_image if Feature.enabled?(:improved_container_scan_matching) @improved_container_scan_matching_enabled
end end
private private
def docker_image_name_without_tag def docker_image_name_without_tag
image_name = default_branch_image.presence || image if improved_container_scan_matching_enabled?
base_name, _, version = image_name.rpartition(':') image_name = default_branch_image.presence || image
base_name, _, version = image_name.rpartition(':')
return image if version_semver_like?(version) return image_name if version_semver_like?(version)
else
base_name, version = image.split(':')
return image if version_semver_like?(version)
end
base_name base_name
end end
......
...@@ -7,18 +7,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -7,18 +7,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
let(:current_branch) { project.default_branch } let(:current_branch) { project.default_branch }
let(:pipeline) { create(:ci_pipeline, ref: current_branch, project: project) } let(:pipeline) { create(:ci_pipeline, ref: current_branch, project: project) }
let(:job) { create(:ci_build, pipeline: pipeline)} let(:job) { create(:ci_build, pipeline: pipeline)}
let(:artifact) { create(:ee_ci_job_artifact, :container_scanning, job: job) }
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) }
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' }
before do shared_examples 'report' 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, 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 it "parses all identifiers and findings for unapproved vulnerabilities" do
expect(report.findings.length).to eq(8) expect(report.findings.length).to eq(8)
expect(report.identifiers.length).to eq(8) expect(report.identifiers.length).to eq(8)
...@@ -34,8 +28,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -34,8 +28,7 @@ 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
...@@ -46,12 +39,40 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -46,12 +39,40 @@ 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
end
context 'with improved_container_scan_matching' do describe '#parse!' do
context 'when improved_container_scan_matching is disabled' do
before do
stub_feature_flags(improved_container_scan_matching: false)
artifact.each_blob { |blob| described_class.parse!(blob, report) }
end
it_behaves_like 'report'
context 'when not on default branch' do
let(:current_branch) { 'not-default' }
it 'does not include default_branch_image' do
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning)
expect(location).to have_attributes(
default_branch_image: nil,
improved_container_scan_matching_enabled?: false
)
end
end
end
context 'when improved_container_scan_matching is enabled' do
before do before do
stub_feature_flags(improved_container_scan_matching: true) stub_feature_flags(improved_container_scan_matching: true)
artifact.each_blob { |blob| described_class.parse!(blob, report) }
end end
it_behaves_like 'report'
context 'when on default branch' do context 'when on default branch' do
let(:current_branch) { project.default_branch } let(:current_branch) { project.default_branch }
...@@ -60,11 +81,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -60,11 +81,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning) expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning)
expect(location).to have_attributes( expect(location).to have_attributes(
image: image, default_branch_image: nil,
operating_system: 'debian:9', improved_container_scan_matching_enabled?: true
package_name: 'glibc',
package_version: '2.24-11+deb9u3',
default_branch_image: nil
) )
end end
end end
...@@ -77,11 +95,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -77,11 +95,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning) expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning)
expect(location).to have_attributes( expect(location).to have_attributes(
image: image, default_branch_image: default_branch_image,
operating_system: 'debian:9', improved_container_scan_matching_enabled?: true
package_name: 'glibc',
package_version: '2.24-11+deb9u3',
default_branch_image: default_branch_image
) )
end end
end end
......
...@@ -24,10 +24,6 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do ...@@ -24,10 +24,6 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do
sha1_of = -> (input) { Digest::SHA1.hexdigest(input) } sha1_of = -> (input) { Digest::SHA1.hexdigest(input) }
context 'with feature enabled' do context 'with feature enabled' do
before do
stub_feature_flags(improved_container_scan_matching: true)
end
where(:image, :default_branch_image, :expected_fingerprint_input) do where(:image, :default_branch_image, :expected_fingerprint_input) do
[ [
['alpine:3.7.3', nil, 'alpine:3.7.3:glibc'], ['alpine:3.7.3', nil, 'alpine:3.7.3:glibc'],
...@@ -58,6 +54,11 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do ...@@ -58,6 +54,11 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do
'registry.gitlab.com/group/project@sha256:a418bbb80b9411f9a08025baa4681e192aaafd16505039bdcb113ccdb90a88fd', 'registry.gitlab.com/group/project@sha256:a418bbb80b9411f9a08025baa4681e192aaafd16505039bdcb113ccdb90a88fd',
'registry.gitlab.com/group/project:latest', 'registry.gitlab.com/group/project:latest',
'registry.gitlab.com/group/project:glibc' 'registry.gitlab.com/group/project:glibc'
],
[
'registry.gitlab.com/group/project/feature:latest',
'registry.gitlab.com/group/project:1.0.0',
'registry.gitlab.com/group/project:1.0.0:glibc'
] ]
] ]
end end
...@@ -69,7 +70,8 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do ...@@ -69,7 +70,8 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do
default_branch_image: default_branch_image, default_branch_image: default_branch_image,
operating_system: 'debian:9', operating_system: 'debian:9',
package_name: 'glibc', package_name: 'glibc',
package_version: '1.2.3' package_version: '1.2.3',
improved_container_scan_matching_enabled: true
} }
end end
...@@ -78,10 +80,6 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do ...@@ -78,10 +80,6 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do
end end
context 'with feature disabled' do context 'with feature disabled' do
before do
stub_feature_flags(improved_container_scan_matching: false)
end
let(:params) do let(:params) do
{ {
image: 'registry.gitlab.com/group/project/feature:ec301f43f14a2b477806875e49cfc4d3fa0d22c3', image: 'registry.gitlab.com/group/project/feature:ec301f43f14a2b477806875e49cfc4d3fa0d22c3',
...@@ -95,6 +93,32 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do ...@@ -95,6 +93,32 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do
it 'ignores default_branch_image' do it 'ignores default_branch_image' do
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/group/project/feature:glibc'))
end end
where(:image, :expected_fingerprint_input) do
[
['alpine:3.7.3', 'alpine:3.7.3:glibc'],
['alpine:3.7', 'alpine:3.7:glibc'],
['alpine:8101518288111119448185914762536722131810', 'alpine:glibc'],
['alpine:1.0.0-beta', 'alpine:1.0.0-beta:glibc'],
[
'registry.gitlab.com/group/project/tmp:af864bd61230d3d694eb01d6205b268b4ad63ac0',
'registry.gitlab.com/group/project/tmp:glibc'
]
]
end
with_them do
let(:params) do
{
image: image,
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '1.2.3'
}
end
specify { expect(subject.fingerprint).to eq(sha1_of.call(expected_fingerprint_input)) }
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