Commit 1e6a860a authored by Aleksei Lipniagov's avatar Aleksei Lipniagov

Merge branch '285120-fix-pipeline-not-pick-license-policies' into 'master'

Fix classification of custom licenses in License Compliance

See merge request gitlab-org/gitlab!71746
parents 7e85d6c5 38f666fa
...@@ -38,24 +38,24 @@ module SCA ...@@ -38,24 +38,24 @@ module SCA
end end
def report_for(policy) def report_for(policy)
build_policy(license_scan_report[policy.software_license.canonical_id], policy) build_policy(reported_license_by_license_model(policy.software_license), policy)
end end
def diff_with(other) def diff_with(other)
license_scan_report license_scanning_report
.diff_with(other.license_scan_report) .diff_with(other.license_scanning_report)
.transform_values do |reported_licenses| .transform_values do |reported_licenses|
reported_licenses.map do |reported_license| reported_licenses.map do |reported_license|
matching_license_policy = matching_license_policy =
known_policies[reported_license.canonical_id] || known_policies[reported_license.id] ||
known_policies[reported_license&.name&.downcase] known_policies[reported_license&.name&.downcase]
build_policy(reported_license, matching_license_policy) build_policy(reported_license, matching_license_policy)
end end
end end
end end
def license_scan_report def license_scanning_report
strong_memoize(:license_scan_report) do strong_memoize(:license_scanning_report) do
pipeline.blank? ? empty_report : pipeline.license_scanning_report pipeline.blank? ? empty_report : pipeline.license_scanning_report
end end
end end
...@@ -74,9 +74,16 @@ module SCA ...@@ -74,9 +74,16 @@ module SCA
end end
end end
# When the license found in the report doesn't match any license
# of the SPDX License List, we need to find it by name explicitly.
def reported_license_by_license_model(software_license)
license_scanning_report[software_license.canonical_id] ||
license_scanning_report.by_license_name(software_license.name&.downcase)
end
def unclassified_policies def unclassified_policies
license_scan_report.licenses.map do |reported_license| license_scanning_report.licenses.map do |reported_license|
next if known_policies[reported_license.canonical_id] next if known_policies[reported_license.id] || known_policies[reported_license&.name&.downcase]
[reported_license.canonical_id, build_policy(reported_license, nil)] [reported_license.canonical_id, build_policy(reported_license, nil)]
end.compact.to_h end.compact.to_h
......
...@@ -34,7 +34,7 @@ module Gitlab ...@@ -34,7 +34,7 @@ module Gitlab
end end
def by_license_name(name) def by_license_name(name)
licenses.find { |license| license.name == name } licenses.find { |license| license.name.casecmp?(name) }
end end
def apply_details_from!(dependency_list_report) def apply_details_from!(dependency_list_report)
......
...@@ -140,6 +140,12 @@ FactoryBot.define do ...@@ -140,6 +140,12 @@ FactoryBot.define do
end end
end end
trait :license_scanning_custom_license do
after :build do |build|
build.job_artifacts << build(:ee_ci_job_artifact, :license_scanning_custom_license, job: build)
end
end
trait :requirements_report do trait :requirements_report do
after(:build) do |build| after(:build) do |build|
build.job_artifacts << create(:ee_ci_job_artifact, :all_passing_requirements, job: build) build.job_artifacts << create(:ee_ci_job_artifact, :all_passing_requirements, job: build)
......
...@@ -159,6 +159,16 @@ FactoryBot.define do ...@@ -159,6 +159,16 @@ FactoryBot.define do
end end
end end
trait :license_scanning_custom_license do
file_type { :license_scanning }
file_format { :raw }
after(:build) do |artifact, _|
artifact.file = fixture_file_upload(
Rails.root.join('ee/spec/fixtures/security_reports/license_compliance/gl-license-scanning-report-custom-license.json'), 'application/json')
end
end
trait :performance do trait :performance do
file_format { :raw } file_format { :raw }
file_type { :performance } file_type { :performance }
...@@ -338,7 +348,7 @@ FactoryBot.define do ...@@ -338,7 +348,7 @@ FactoryBot.define do
trait :"v#{version}" do trait :"v#{version}" do
after(:build) do |artifact, _| after(:build) do |artifact, _|
filename = "gl-#{artifact.file_type.dasherize}-report-v#{version.sub(/_/, '.')}.json" filename = "gl-#{artifact.file_type.dasherize}-report-v#{version.sub(/_/, '.')}.json"
path = Rails.root.join("ee/spec/fixtures/security_reports/#{filename}") path = Rails.root.join("ee/spec/fixtures/security_reports/license_compliance/#{filename}")
artifact.file = fixture_file_upload(path, "application/json") artifact.file = fixture_file_upload(path, "application/json")
end end
end end
......
...@@ -30,7 +30,7 @@ RSpec.describe 'EE > Projects > Licenses > Maintainer views policies', :js do ...@@ -30,7 +30,7 @@ RSpec.describe 'EE > Projects > Licenses > Maintainer views policies', :js do
let_it_be(:mit_policy) { create(:software_license_policy, :denied, software_license: mit, project: project) } let_it_be(:mit_policy) { create(:software_license_policy, :denied, software_license: mit, project: project) }
let_it_be(:pipeline) { create(:ee_ci_pipeline, project: project, builds: [create(:ee_ci_build, :license_scan_v2, :success)], status: :success) } let_it_be(:pipeline) { create(:ee_ci_pipeline, project: project, builds: [create(:ee_ci_build, :license_scan_v2, :success)], status: :success) }
let(:report) { Gitlab::Json.parse(fixture_file('security_reports/gl-license-scanning-report-v2.json', dir: 'ee')) } let(:report) { Gitlab::Json.parse(fixture_file('security_reports/license_compliance/gl-license-scanning-report-v2.json', dir: 'ee')) }
let(:known_licenses) { report['licenses'].find_all { |license| license['url'].present? } } let(:known_licenses) { report['licenses'].find_all { |license| license['url'].present? } }
it 'displays licenses detected in the most recent scan report' do it 'displays licenses detected in the most recent scan report' do
......
{
"version": "2.1",
"licenses": [
{
"id": "BSD-3-Clause",
"name": "BSD 3-Clause \"New\" or \"Revised\" License",
"url": "https://opensource.org/licenses/BSD-3-Clause"
},
{
"id": "MIT",
"name": "MIT License",
"url": "https://opensource.org/licenses/MIT"
},
{
"id": "foo",
"name": "Foo License",
"url": ""
}
],
"dependencies": [
{
"name": "a",
"version": "1.0.0",
"package_manager": "bundler",
"path": "Gemfile.lock",
"licenses": ["MIT"]
},
{
"name": "b",
"version": "0.1.0",
"package_manager": "yarn",
"path": "yarn.lock",
"licenses": ["BSD-3-Clause"]
},
{
"name": "c",
"version": "1.1.0",
"package_manager": "bundler",
"path": "Gemfile.lock",
"licenses": ["MIT", "BSD-3-Clause"]
},
{
"name": "d",
"version": "1.1.1",
"package_manager": "bundler",
"path": "Gemfile.lock",
"licenses": ["foo"]
}
]
}
...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning do ...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning do
end end
context 'when parsing a valid v1.1 report' do context 'when parsing a valid v1.1 report' do
let(:v1_1_data) { fixture_file('security_reports/gl-license-scanning-report-v1.1.json', dir: 'ee') } let(:v1_1_data) { fixture_file('security_reports/license_compliance/gl-license-scanning-report-v1.1.json', dir: 'ee') }
before do before do
subject.parse!(v1_1_data, report) subject.parse!(v1_1_data, report)
...@@ -74,7 +74,7 @@ RSpec.describe Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning do ...@@ -74,7 +74,7 @@ RSpec.describe Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning do
end end
context 'when parsing a valid v2 report' do context 'when parsing a valid v2 report' do
let(:v2_0_data) { fixture_file('security_reports/gl-license-scanning-report-v2.json', dir: 'ee') } let(:v2_0_data) { fixture_file('security_reports/license_compliance/gl-license-scanning-report-v2.json', dir: 'ee') }
before do before do
subject.parse!(v2_0_data, report) subject.parse!(v2_0_data, report)
...@@ -106,7 +106,7 @@ RSpec.describe Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning do ...@@ -106,7 +106,7 @@ RSpec.describe Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning do
end end
context 'when parsing a valid v2.1 report' do context 'when parsing a valid v2.1 report' do
let(:v2_1_data) { fixture_file('security_reports/gl-license-scanning-report-v2.1.json', dir: 'ee') } let(:v2_1_data) { fixture_file('security_reports/license_compliance/gl-license-scanning-report-v2.1.json', dir: 'ee') }
before do before do
subject.parse!(v2_1_data, report) subject.parse!(v2_1_data, report)
......
...@@ -11,7 +11,7 @@ RSpec.describe Gitlab::Ci::Reports::LicenseScanning::Report do ...@@ -11,7 +11,7 @@ RSpec.describe Gitlab::Ci::Reports::LicenseScanning::Report do
let(:report) { build(:ci_reports_license_scanning_report, :report_2) } let(:report) { build(:ci_reports_license_scanning_report, :report_2) }
context 'with existing license' do context 'with existing license' do
let(:name) { 'MIT' } let(:name) { 'MIt' }
it 'finds right name' do it 'finds right name' do
is_expected.to be_a(Gitlab::Ci::Reports::LicenseScanning::License) is_expected.to be_a(Gitlab::Ci::Reports::LicenseScanning::License)
...@@ -290,7 +290,7 @@ RSpec.describe Gitlab::Ci::Reports::LicenseScanning::Report do ...@@ -290,7 +290,7 @@ RSpec.describe Gitlab::Ci::Reports::LicenseScanning::Report do
context 'when parsing a v2 report' do context 'when parsing a v2 report' do
subject { described_class.parse_from(v2_json) } subject { described_class.parse_from(v2_json) }
let(:v2_json) { fixture_file('security_reports/gl-license-scanning-report-v2.json', dir: 'ee') } let(:v2_json) { fixture_file('security_reports/license_compliance/gl-license-scanning-report-v2.json', dir: 'ee') }
it { expect(subject.version).to eql('2.0') } it { expect(subject.version).to eql('2.0') }
it { expect(subject.licenses.count).to eq(3) } it { expect(subject.licenses.count).to eq(3) }
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
require "spec_helper" require "spec_helper"
RSpec.describe SCA::LicenseCompliance do RSpec.describe SCA::LicenseCompliance do
subject { project.license_compliance } let(:license_compliance) { described_class.new(project, pipeline) }
let_it_be(:project) { create(:project, :repository, :private) }
let(:project) { create(:project, :repository, :private) }
let(:mit) { create(:software_license, :mit) } let(:mit) { create(:software_license, :mit) }
let(:other_license) { create(:software_license, name: "SOFTWARE-LICENSE", spdx_identifier: "Other-Id") } let(:other_license) { create(:software_license, name: "SOFTWARE-LICENSE", spdx_identifier: "Other-Id") }
...@@ -14,44 +15,48 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -14,44 +15,48 @@ RSpec.describe SCA::LicenseCompliance do
end end
describe "#policies" do describe "#policies" do
subject(:policies) { license_compliance.policies }
context "when a pipeline has not been run for this project" do context "when a pipeline has not been run for this project" do
it { expect(subject.policies.count).to be_zero } let(:pipeline) { nil }
it { expect(policies.count).to be_zero }
context "when the project has policies configured" do context "when the project has policies configured" do
let!(:mit_policy) { create(:software_license_policy, :denied, software_license: mit, project: project) } let!(:mit_policy) { create(:software_license_policy, :denied, software_license: mit, project: project) }
it "includes an a policy for a classified license that was not detected in the scan report" do it "includes an a policy for a classified license that was not detected in the scan report" do
expect(subject.policies.count).to be(1) expect(policies.count).to eq(1)
expect(subject.policies[0].id).to eq(mit_policy.id) expect(policies[0].id).to eq(mit_policy.id)
expect(subject.policies[0].name).to eq(mit.name) expect(policies[0].name).to eq(mit.name)
expect(subject.policies[0].url).to be_nil expect(policies[0].url).to be_nil
expect(subject.policies[0].classification).to eq("denied") expect(policies[0].classification).to eq("denied")
expect(subject.policies[0].spdx_identifier).to eq(mit.spdx_identifier) expect(policies[0].spdx_identifier).to eq(mit.spdx_identifier)
end end
end end
end end
context "when a pipeline has run" do context "when a pipeline has run" do
let!(:pipeline) { create(:ci_pipeline, :success, project: project, builds: builds) } let(:pipeline) { create(:ci_pipeline, :success, project: project, builds: builds) }
let(:builds) { [] } let(:builds) { [] }
context "when a license scan job is not configured" do context "when a license scan job is not configured" do
let(:builds) { [create(:ci_build, :success)] } let(:builds) { [create(:ci_build, :success)] }
it { expect(subject.policies).to be_empty } it { expect(policies).to be_empty }
end end
context "when the license scan job has not finished" do context "when the license scan job has not finished" do
let(:builds) { [create(:ci_build, :running, job_artifacts: [artifact])] } let(:builds) { [create(:ci_build, :running, job_artifacts: [artifact])] }
let(:artifact) { create(:ci_job_artifact, file_type: :license_scanning, file_format: :raw) } let(:artifact) { create(:ci_job_artifact, file_type: :license_scanning, file_format: :raw) }
it { expect(subject.policies).to be_empty } it { expect(policies).to be_empty }
end end
context "when the license scan produces a poorly formatted report" do context "when the license scan produces a poorly formatted report" do
let(:builds) { [create(:ee_ci_build, :running, :corrupted_license_scanning_report)] } let(:builds) { [create(:ee_ci_build, :running, :corrupted_license_scanning_report)] }
it { expect(subject.policies).to be_empty } it { expect(policies).to be_empty }
end end
context "when the dependency scan produces a poorly formatted report" do context "when the dependency scan produces a poorly formatted report" do
...@@ -62,7 +67,7 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -62,7 +67,7 @@ RSpec.describe SCA::LicenseCompliance do
] ]
end end
it { expect(subject.policies.map(&:spdx_identifier)).to contain_exactly("BSD-3-Clause", "MIT", nil) } it { expect(policies.map(&:spdx_identifier)).to contain_exactly("BSD-3-Clause", "MIT", nil) }
end end
context "when a pipeline has successfully produced a v2.0 license scan report" do context "when a pipeline has successfully produced a v2.0 license scan report" do
...@@ -71,39 +76,39 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -71,39 +76,39 @@ RSpec.describe SCA::LicenseCompliance do
let!(:other_license_policy) { create(:software_license_policy, :allowed, software_license: other_license, project: project) } let!(:other_license_policy) { create(:software_license_policy, :allowed, software_license: other_license, project: project) }
it "includes a policy for each detected license and classified license" do it "includes a policy for each detected license and classified license" do
expect(subject.policies.count).to eq(4) expect(policies.count).to eq(4)
end end
it 'includes a policy for a detected license that is unclassified' do it 'includes a policy for a detected license that is unclassified' do
expect(subject.policies[0].id).to be_nil expect(policies[0].id).to be_nil
expect(subject.policies[0].name).to eq("BSD 3-Clause \"New\" or \"Revised\" License") expect(policies[0].name).to eq("BSD 3-Clause \"New\" or \"Revised\" License")
expect(subject.policies[0].url).to eq("http://spdx.org/licenses/BSD-3-Clause.json") expect(policies[0].url).to eq("http://spdx.org/licenses/BSD-3-Clause.json")
expect(subject.policies[0].classification).to eq("unclassified") expect(policies[0].classification).to eq("unclassified")
expect(subject.policies[0].spdx_identifier).to eq("BSD-3-Clause") expect(policies[0].spdx_identifier).to eq("BSD-3-Clause")
end end
it 'includes a policy for a classified license that was also detected in the scan report' do it 'includes a policy for a classified license that was also detected in the scan report' do
expect(subject.policies[1].id).to eq(mit_policy.id) expect(policies[1].id).to eq(mit_policy.id)
expect(subject.policies[1].name).to eq(mit.name) expect(policies[1].name).to eq(mit.name)
expect(subject.policies[1].url).to eq("http://spdx.org/licenses/MIT.json") expect(policies[1].url).to eq("http://spdx.org/licenses/MIT.json")
expect(subject.policies[1].classification).to eq("denied") expect(policies[1].classification).to eq("denied")
expect(subject.policies[1].spdx_identifier).to eq("MIT") expect(policies[1].spdx_identifier).to eq("MIT")
end end
it 'includes a policy for a classified license that was not detected in the scan report' do it 'includes a policy for a classified license that was not detected in the scan report' do
expect(subject.policies[2].id).to eq(other_license_policy.id) expect(policies[2].id).to eq(other_license_policy.id)
expect(subject.policies[2].name).to eq(other_license.name) expect(policies[2].name).to eq(other_license.name)
expect(subject.policies[2].url).to be_blank expect(policies[2].url).to be_blank
expect(subject.policies[2].classification).to eq("allowed") expect(policies[2].classification).to eq("allowed")
expect(subject.policies[2].spdx_identifier).to eq(other_license.spdx_identifier) expect(policies[2].spdx_identifier).to eq(other_license.spdx_identifier)
end end
it 'includes a policy for an unclassified and unknown license that was detected in the scan report' do it 'includes a policy for an unclassified and unknown license that was detected in the scan report' do
expect(subject.policies[3].id).to be_nil expect(policies[3].id).to be_nil
expect(subject.policies[3].name).to eq("unknown") expect(policies[3].name).to eq("unknown")
expect(subject.policies[3].url).to be_blank expect(policies[3].url).to be_blank
expect(subject.policies[3].classification).to eq("unclassified") expect(policies[3].classification).to eq("unclassified")
expect(subject.policies[3].spdx_identifier).to be_nil expect(policies[3].spdx_identifier).to be_nil
end end
end end
...@@ -113,39 +118,39 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -113,39 +118,39 @@ RSpec.describe SCA::LicenseCompliance do
let!(:other_license_policy) { create(:software_license_policy, :allowed, software_license: other_license, project: project) } let!(:other_license_policy) { create(:software_license_policy, :allowed, software_license: other_license, project: project) }
it "includes a policy for each detected license and classified license" do it "includes a policy for each detected license and classified license" do
expect(subject.policies.count).to eq(4) expect(policies.count).to eq(4)
end end
it 'includes a policy for a detected license that is unclassified' do it 'includes a policy for a detected license that is unclassified' do
expect(subject.policies[0].id).to be_nil expect(policies[0].id).to be_nil
expect(subject.policies[0].name).to eq("BSD 3-Clause \"New\" or \"Revised\" License") expect(policies[0].name).to eq("BSD 3-Clause \"New\" or \"Revised\" License")
expect(subject.policies[0].url).to eq("https://opensource.org/licenses/BSD-3-Clause") expect(policies[0].url).to eq("https://opensource.org/licenses/BSD-3-Clause")
expect(subject.policies[0].classification).to eq("unclassified") expect(policies[0].classification).to eq("unclassified")
expect(subject.policies[0].spdx_identifier).to eq("BSD-3-Clause") expect(policies[0].spdx_identifier).to eq("BSD-3-Clause")
end end
it 'includes a policy for a classified license that was also detected in the scan report' do it 'includes a policy for a classified license that was also detected in the scan report' do
expect(subject.policies[1].id).to eq(mit_policy.id) expect(policies[1].id).to eq(mit_policy.id)
expect(subject.policies[1].name).to eq(mit.name) expect(policies[1].name).to eq(mit.name)
expect(subject.policies[1].url).to eq("https://opensource.org/licenses/MIT") expect(policies[1].url).to eq("https://opensource.org/licenses/MIT")
expect(subject.policies[1].classification).to eq("denied") expect(policies[1].classification).to eq("denied")
expect(subject.policies[1].spdx_identifier).to eq("MIT") expect(policies[1].spdx_identifier).to eq("MIT")
end end
it 'includes a policy for a classified license that was not detected in the scan report' do it 'includes a policy for a classified license that was not detected in the scan report' do
expect(subject.policies[2].id).to eq(other_license_policy.id) expect(policies[2].id).to eq(other_license_policy.id)
expect(subject.policies[2].name).to eq(other_license.name) expect(policies[2].name).to eq(other_license.name)
expect(subject.policies[2].url).to be_blank expect(policies[2].url).to be_blank
expect(subject.policies[2].classification).to eq("allowed") expect(policies[2].classification).to eq("allowed")
expect(subject.policies[2].spdx_identifier).to eq(other_license.spdx_identifier) expect(policies[2].spdx_identifier).to eq(other_license.spdx_identifier)
end end
it 'includes a policy for an unclassified and unknown license that was detected in the scan report' do it 'includes a policy for an unclassified and unknown license that was detected in the scan report' do
expect(subject.policies[3].id).to be_nil expect(policies[3].id).to be_nil
expect(subject.policies[3].name).to eq("unknown") expect(policies[3].name).to eq("unknown")
expect(subject.policies[3].url).to be_blank expect(policies[3].url).to be_blank
expect(subject.policies[3].classification).to eq("unclassified") expect(policies[3].classification).to eq("unclassified")
expect(subject.policies[3].spdx_identifier).to be_nil expect(policies[3].spdx_identifier).to be_nil
end end
end end
...@@ -155,35 +160,35 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -155,35 +160,35 @@ RSpec.describe SCA::LicenseCompliance do
let!(:other_license_policy) { create(:software_license_policy, :allowed, software_license: other_license, project: project) } let!(:other_license_policy) { create(:software_license_policy, :allowed, software_license: other_license, project: project) }
it 'includes a policy for an unclassified license detected in the scan report' do it 'includes a policy for an unclassified license detected in the scan report' do
expect(subject.policies[0].id).to be_nil expect(policies[0].id).to be_nil
expect(subject.policies[0].name).to eq("BSD") expect(policies[0].name).to eq("BSD")
expect(subject.policies[0].url).to eq("http://spdx.org/licenses/BSD-4-Clause.json") expect(policies[0].url).to eq("http://spdx.org/licenses/BSD-4-Clause.json")
expect(subject.policies[0].classification).to eq("unclassified") expect(policies[0].classification).to eq("unclassified")
expect(subject.policies[0].spdx_identifier).to eq("BSD-4-Clause") expect(policies[0].spdx_identifier).to eq("BSD-4-Clause")
end end
it 'includes a policy for a denied license found in the scan report' do it 'includes a policy for a denied license found in the scan report' do
expect(subject.policies[1].id).to eq(mit_policy.id) expect(policies[1].id).to eq(mit_policy.id)
expect(subject.policies[1].name).to eq(mit.name) expect(policies[1].name).to eq(mit.name)
expect(subject.policies[1].url).to eq("http://opensource.org/licenses/mit-license") expect(policies[1].url).to eq("http://opensource.org/licenses/mit-license")
expect(subject.policies[1].classification).to eq("denied") expect(policies[1].classification).to eq("denied")
expect(subject.policies[1].spdx_identifier).to eq("MIT") expect(policies[1].spdx_identifier).to eq("MIT")
end end
it 'includes a policy for an allowed license NOT found in the scan report' do it 'includes a policy for an allowed license NOT found in the scan report' do
expect(subject.policies[2].id).to eq(other_license_policy.id) expect(policies[2].id).to eq(other_license_policy.id)
expect(subject.policies[2].name).to eq(other_license.name) expect(policies[2].name).to eq(other_license.name)
expect(subject.policies[2].url).to be_blank expect(policies[2].url).to be_blank
expect(subject.policies[2].classification).to eq("allowed") expect(policies[2].classification).to eq("allowed")
expect(subject.policies[2].spdx_identifier).to eq(other_license.spdx_identifier) expect(policies[2].spdx_identifier).to eq(other_license.spdx_identifier)
end end
it 'includes a policy for an unclassified and unknown license found in the scan report' do it 'includes a policy for an unclassified and unknown license found in the scan report' do
expect(subject.policies[3].id).to be_nil expect(policies[3].id).to be_nil
expect(subject.policies[3].name).to eq("unknown") expect(policies[3].name).to eq("unknown")
expect(subject.policies[3].url).to be_blank expect(policies[3].url).to be_blank
expect(subject.policies[3].classification).to eq("unclassified") expect(policies[3].classification).to eq("unclassified")
expect(subject.policies[3].spdx_identifier).to be_nil expect(policies[3].spdx_identifier).to be_nil
end end
end end
end end
...@@ -202,10 +207,10 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -202,10 +207,10 @@ RSpec.describe SCA::LicenseCompliance do
end end
context "when searching for policies for licenses that were detected in a scan report" do context "when searching for policies for licenses that were detected in a scan report" do
let(:results) { subject.find_policies(detected_only: true) } let(:results) { license_compliance.find_policies(detected_only: true) }
it 'only includes licenses that appear in the latest license scan report' do it 'only includes licenses that appear in the latest license scan report' do
expect(results.count).to be(3) expect(results.count).to eq(3)
end end
it 'includes a policy for an unclassified and known license that was detected in the scan report' do it 'includes a policy for an unclassified and known license that was detected in the scan report' do
...@@ -240,10 +245,22 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -240,10 +245,22 @@ RSpec.describe SCA::LicenseCompliance do
spdx_identifier: nil spdx_identifier: nil
) )
end end
context "with denied license without spdx identifier" do
let!(:pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :success, :license_scanning_custom_license)]) }
let(:custom_license) { create(:software_license, :user_entered, name: "foO licensE") }
let!(:custom_license_policy) { create(:software_license_policy, :denied, software_license: custom_license, project: project) }
let(:results) { license_compliance.find_policies(detected_only: true) }
it 'contains denied license' do
expect(results.count).to eq(3)
end
end
end end
context "when searching for policies with a specific classification" do context "when searching for policies with a specific classification" do
let(:results) { subject.find_policies(classification: ['allowed']) } let(:results) { license_compliance.find_policies(classification: ['allowed']) }
it 'includes an entry for each `allowed` licensed' do it 'includes an entry for each `allowed` licensed' do
expect(results.count).to eq(1) expect(results.count).to eq(1)
...@@ -259,7 +276,7 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -259,7 +276,7 @@ RSpec.describe SCA::LicenseCompliance do
end end
context "when searching for policies by multiple classifications" do context "when searching for policies by multiple classifications" do
let(:results) { subject.find_policies(classification: %w[allowed denied]) } let(:results) { license_compliance.find_policies(classification: %w[allowed denied]) }
it 'includes an entry for each `allowed` and `denied` licensed' do it 'includes an entry for each `allowed` and `denied` licensed' do
expect(results.count).to eq(2) expect(results.count).to eq(2)
...@@ -283,7 +300,7 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -283,7 +300,7 @@ RSpec.describe SCA::LicenseCompliance do
end end
context "when searching for detected policies matching a classification" do context "when searching for detected policies matching a classification" do
let(:results) { subject.find_policies(detected_only: true, classification: %w[allowed denied]) } let(:results) { license_compliance.find_policies(detected_only: true, classification: %w[allowed denied]) }
it 'includes an entry for each entry that was detected in the report and matches a classification' do it 'includes an entry for each entry that was detected in the report and matches a classification' do
expect(results.count).to eq(1) expect(results.count).to eq(1)
...@@ -317,65 +334,68 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -317,65 +334,68 @@ RSpec.describe SCA::LicenseCompliance do
end end
with_them do with_them do
let(:results) { subject.find_policies(sort: { by: attribute, direction: direction }) } let(:results) { license_compliance.find_policies(sort: { by: attribute, direction: direction }) }
it { expect(results.map(&:name)).to eq(expected) } it { expect(results.map(&:name)).to eq(expected) }
end end
context 'when using the default sort options' do context 'when using the default sort options' do
it { expect(subject.find_policies.map(&:name)).to eq(sorted_by_name_asc) } it { expect(license_compliance.find_policies.map(&:name)).to eq(sorted_by_name_asc) }
end end
context 'when `nil` sort options are provided' do context 'when `nil` sort options are provided' do
it { expect(subject.find_policies(sort: nil).map(&:name)).to eq(sorted_by_name_asc) } it { expect(license_compliance.find_policies(sort: nil).map(&:name)).to eq(sorted_by_name_asc) }
end end
end end
end end
describe "#latest_build_for_default_branch" do describe "#latest_build_for_default_branch" do
subject { license_compliance.latest_build_for_default_branch }
let(:pipeline) { nil }
let(:regular_build) { create(:ci_build, :success) } let(:regular_build) { create(:ci_build, :success) }
let(:license_scan_build) { create(:ee_ci_build, :license_scan_v2_1, :success) } let(:license_scan_build) { create(:ee_ci_build, :license_scan_v2_1, :success) }
context "when a pipeline has never been completed for the project" do context "when a pipeline has never been completed for the project" do
it { expect(subject.latest_build_for_default_branch).to be_nil } let(:pipeline) { nil }
it { is_expected.to be_nil }
end end
context "when a pipeline has completed successfully and produced a license scan report" do context "when a pipeline has completed successfully and produced a license scan report" do
let!(:pipeline) { create(:ci_pipeline, :success, project: project, builds: [regular_build, license_scan_build]) } let!(:pipeline) { create(:ci_pipeline, :success, project: project, builds: [regular_build, license_scan_build]) }
it { expect(subject.latest_build_for_default_branch).to eq(license_scan_build) } it { is_expected.to eq(license_scan_build) }
end end
context "when a pipeline has completed but does not contain a license scan report" do context "when a pipeline has completed but does not contain a license scan report" do
let!(:pipeline) { create(:ci_pipeline, :success, project: project, builds: [regular_build]) } let!(:pipeline) { create(:ci_pipeline, :success, project: project, builds: [regular_build]) }
it { expect(subject.latest_build_for_default_branch).to be_nil } it { is_expected.to be_nil }
end
context "when latest pipeline doesn't contain license job" do
let!(:pipeline1) { create(:ci_pipeline, :success, project: project, builds: [license_scan_build]) }
let!(:pipeline2) { create(:ci_pipeline, :success, project: project, builds: [regular_build]) }
it { expect(subject.latest_build_for_default_branch).to eq(license_scan_build) }
end end
end end
describe "#diff_with" do describe "#diff_with" do
context "when the head pipeline has not run" do context "when the head pipeline has not run" do
subject { project.license_compliance(nil).diff_with(base_compliance) } subject(:diff) { license_compliance.diff_with(base_compliance) }
let(:pipeline) { nil }
let!(:base_compliance) { project.license_compliance(base_pipeline) } let!(:base_compliance) { project.license_compliance(base_pipeline) }
let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [license_scan_build]) } let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [license_scan_build]) }
let(:license_scan_build) { create(:ee_ci_build, :license_scan_v2_1, :success) } let(:license_scan_build) { create(:ee_ci_build, :license_scan_v2_1, :success) }
specify { expect(subject[:added]).to all(be_instance_of(::SCA::LicensePolicy)) } specify { expect(diff[:added]).to all(be_instance_of(::SCA::LicensePolicy)) }
specify { expect(subject[:added].count).to eq(3) } specify { expect(diff[:added].count).to eq(3) }
specify { expect(subject[:removed]).to be_empty } specify { expect(diff[:removed]).to be_empty }
specify { expect(subject[:unchanged]).to be_empty } specify { expect(diff[:unchanged]).to be_empty }
end end
context "when nothing has changed between the head and the base pipeline" do context "when nothing has changed between the head and the base pipeline" do
subject { project.license_compliance(head_pipeline).diff_with(base_compliance) } subject(:diff) { license_compliance.diff_with(base_compliance) }
let(:pipeline) { head_pipeline }
let!(:head_compliance) { project.license_compliance(head_pipeline) } let!(:head_compliance) { project.license_compliance(head_pipeline) }
let!(:head_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) } let!(:head_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) }
...@@ -383,14 +403,16 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -383,14 +403,16 @@ RSpec.describe SCA::LicenseCompliance do
let!(:base_compliance) { project.license_compliance(base_pipeline) } let!(:base_compliance) { project.license_compliance(base_pipeline) }
let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) } let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) }
specify { expect(subject[:added]).to be_empty } specify { expect(diff[:added]).to be_empty }
specify { expect(subject[:removed]).to be_empty } specify { expect(diff[:removed]).to be_empty }
specify { expect(subject[:unchanged]).to all(be_instance_of(::SCA::LicensePolicy)) } specify { expect(diff[:unchanged]).to all(be_instance_of(::SCA::LicensePolicy)) }
specify { expect(subject[:unchanged].count).to eq(3) } specify { expect(diff[:unchanged].count).to eq(3) }
end end
context "when the base pipeline removed some licenses" do context "when the base pipeline removed some licenses" do
subject { project.license_compliance(head_pipeline).diff_with(base_compliance) } subject(:diff) { license_compliance.diff_with(base_compliance) }
let(:pipeline) { head_pipeline }
let!(:head_compliance) { project.license_compliance(head_pipeline) } let!(:head_compliance) { project.license_compliance(head_pipeline) }
let!(:head_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) } let!(:head_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) }
...@@ -398,14 +420,16 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -398,14 +420,16 @@ RSpec.describe SCA::LicenseCompliance do
let!(:base_compliance) { project.license_compliance(base_pipeline) } let!(:base_compliance) { project.license_compliance(base_pipeline) }
let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :success)]) } let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :success)]) }
specify { expect(subject[:added]).to be_empty } specify { expect(diff[:added]).to be_empty }
specify { expect(subject[:unchanged]).to be_empty } specify { expect(diff[:unchanged]).to be_empty }
specify { expect(subject[:removed]).to all(be_instance_of(::SCA::LicensePolicy)) } specify { expect(diff[:removed]).to all(be_instance_of(::SCA::LicensePolicy)) }
specify { expect(subject[:removed].count).to eq(3) } specify { expect(diff[:removed].count).to eq(3) }
end end
context "when the base pipeline added some licenses" do context "when the base pipeline added some licenses" do
subject { project.license_compliance(head_pipeline).diff_with(base_compliance) } subject(:diff) { license_compliance.diff_with(base_compliance) }
let(:pipeline) { head_pipeline }
let!(:head_compliance) { project.license_compliance(head_pipeline) } let!(:head_compliance) { project.license_compliance(head_pipeline) }
let!(:head_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :success)]) } let!(:head_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :success)]) }
...@@ -413,17 +437,17 @@ RSpec.describe SCA::LicenseCompliance do ...@@ -413,17 +437,17 @@ RSpec.describe SCA::LicenseCompliance do
let!(:base_compliance) { project.license_compliance(base_pipeline) } let!(:base_compliance) { project.license_compliance(base_pipeline) }
let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) } let!(:base_pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :license_scan_v2_1, :success)]) }
specify { expect(subject[:added]).to all(be_instance_of(::SCA::LicensePolicy)) } specify { expect(diff[:added]).to all(be_instance_of(::SCA::LicensePolicy)) }
specify { expect(subject[:added].count).to eq(3) } specify { expect(diff[:added].count).to eq(3) }
specify { expect(subject[:removed]).to be_empty } specify { expect(diff[:removed]).to be_empty }
specify { expect(subject[:unchanged]).to be_empty } specify { expect(diff[:unchanged]).to be_empty }
context "when a software license record does not have an spdx identifier" do context "when a software license record does not have an spdx identifier" do
let(:license_name) { 'MIT License' } let(:license_name) { 'MIT License' }
let!(:policy) { create(:software_license_policy, :allowed, project: project, software_license: create(:software_license, name: license_name)) } let!(:policy) { create(:software_license_policy, :allowed, project: project, software_license: create(:software_license, name: license_name)) }
it "falls back to matching detections based on name rather than spdx id" do it "falls back to matching detections based on name rather than spdx id" do
mit = subject[:added].find { |item| item.name == license_name } mit = diff[:added].find { |item| item.name == license_name }
expect(mit).to be_present expect(mit).to be_present
expect(mit.classification).to eql('allowed') expect(mit.classification).to eql('allowed')
......
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