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
if improved_container_scan_matching_enabled?
image_name = default_branch_image.presence || image image_name = default_branch_image.presence || image
base_name, _, version = image_name.rpartition(':') base_name, _, version = image_name.rpartition(':')
return image_name if version_semver_like?(version)
else
base_name, version = image.split(':')
return image if version_semver_like?(version) 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(: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, job: job) } 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(: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' } let(:default_branch_image) { 'registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest' }
shared_examples 'report' do
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