Commit 8854f527 authored by Imre Farkas's avatar Imre Farkas

Merge branch '14061-policies-filter' into 'master'

Filter software license policies by classification

See merge request gitlab-org/gitlab!22554
parents d4278239 1acf94ba
......@@ -65,12 +65,20 @@ module Projects
render json: { errors: result[:message].as_json }, status: result.fetch(:http_status, :unprocessable_entity)
end
def matching_policies_params
params.permit(:detected, classification: [])
end
def matching_policies_from(license_compliance)
if params[:detected]
license_compliance.detected_policies
else
license_compliance.policies
filters = matching_policies_params
license_compliance.find_policies(
detected_only: truthy?(filters[:detected]),
classification: filters[:classification]
)
end
def truthy?(value)
value.in?(%w[true 1])
end
end
end
......@@ -14,8 +14,12 @@ module SCA
end
end
def detected_policies
policies.reject { |policy| policy.dependencies.count.zero? }
def find_policies(detected_only: false, classification: [])
classifications = Array(classification || [])
policies.reject do |policy|
(detected_only && policy.dependencies.none?) ||
(classifications.present? && !policy.classification.in?(classifications))
end
end
def latest_build_for_default_branch
......
......@@ -84,7 +84,11 @@ describe Projects::LicensesController do
context "when loading all policies" do
before do
get :index, params: { namespace_id: project.namespace, project_id: project }, format: :json
get :index, params: {
namespace_id: project.namespace,
project_id: project,
detected: false
}, format: :json
end
it { expect(response).to have_http_status(:ok) }
......@@ -172,6 +176,56 @@ describe Projects::LicensesController do
})
end
end
context "when loading `allowed` software policies only" do
before do
get :index, params: {
namespace_id: project.namespace,
project_id: project,
classification: ['allowed']
}, format: :json
end
it { expect(response).to have_http_status(:ok) }
it { expect(json_response["licenses"].count).to be(1) }
it 'includes only `allowed` policies' do
expect(json_response.dig("licenses", 0)).to include({
"id" => other_license_policy.id,
"spdx_identifier" => "Other-Id",
"classification" => "allowed"
})
end
end
context "when loading `allowed` and `denied` software policies" do
before do
get :index, params: {
namespace_id: project.namespace,
project_id: project,
classification: %w[allowed denied]
}, format: :json
end
it { expect(response).to have_http_status(:ok) }
it { expect(json_response["licenses"].count).to be(2) }
it 'includes `denied` policies' do
expect(json_response.dig("licenses", 0)).to include({
"id" => mit_policy.id,
"spdx_identifier" => mit.spdx_identifier,
"classification" => mit_policy.classification
})
end
it 'includes `allowed` policies' do
expect(json_response.dig("licenses", 1)).to include({
"id" => other_license_policy.id,
"spdx_identifier" => other_license_policy.spdx_identifier,
"classification" => other_license_policy.classification
})
end
end
end
context 'without existing report' do
......
......@@ -147,38 +147,113 @@ RSpec.describe SCA::LicenseCompliance do
end
end
describe "#detected_policies" do
describe "#find_policies" do
let!(:pipeline) { create(:ci_pipeline, :success, project: project, builds: [create(:ee_ci_build, :success, :license_scan_v2)]) }
let!(:mit_policy) { create(:software_license_policy, :denied, software_license: mit, project: project) }
let!(:other_license_policy) { create(:software_license_policy, :allowed, software_license: other_license, project: project) }
let(:results) { subject.detected_policies }
it 'excludes policies for licenses that do not appear in the latest license scan report' do
expect(results.count).to eq(3)
def assert_matches(item, expected = {})
actual = expected.keys.each_with_object({}) do |attribute, memo|
memo[attribute] = item.public_send(attribute)
end
expect(actual).to eql(expected)
end
context "when searching for policies for licenses that were detected in a scan report" do
let(:results) { subject.find_policies(detected_only: true) }
it 'only includes licenses that appear in the latest license scan report' do
expect(results.count).to be(3)
end
it 'includes a policy for an unclassified and known license that was detected in the scan report' do
expect(results[0].id).to be_nil
expect(results[0].name).to eq("BSD 3-Clause \"New\" or \"Revised\" License")
expect(results[0].url).to eq("http://spdx.org/licenses/BSD-3-Clause.json")
expect(results[0].classification).to eq("unclassified")
expect(results[0].spdx_identifier).to eq("BSD-3-Clause")
assert_matches(
results[0],
id: nil,
name: 'BSD 3-Clause "New" or "Revised" License',
url: "http://spdx.org/licenses/BSD-3-Clause.json",
classification: "unclassified",
spdx_identifier: "BSD-3-Clause"
)
end
it 'includes an entry for a denied license found in the scan report' do
expect(results[1].id).to eq(mit_policy.id)
expect(results[1].name).to eq(mit.name)
expect(results[1].url).to eq("http://spdx.org/licenses/MIT.json")
expect(results[1].classification).to eq("denied")
expect(results[1].spdx_identifier).to eq("MIT")
assert_matches(
results[1],
id: mit_policy.id,
name: mit.name,
url: "http://spdx.org/licenses/MIT.json",
classification: "denied",
spdx_identifier: "MIT"
)
end
it 'includes an entry for an allowed license found in the scan report' do
expect(results[2].id).to be_nil
expect(results[2].name).to eq("unknown")
expect(results[2].url).to be_blank
expect(results[2].classification).to eq("unclassified")
expect(results[2].spdx_identifier).to be_nil
assert_matches(
results[2],
id: nil,
name: 'unknown',
url: '',
classification: 'unclassified',
spdx_identifier: nil
)
end
end
context "when searching for policies with a specific classification" do
let(:results) { subject.find_policies(classification: ['allowed']) }
it 'includes an entry for each `allowed` licensed' do
expect(results.count).to eq(1)
assert_matches(
results[0],
id: other_license_policy.id,
name: other_license_policy.software_license.name,
url: nil,
classification: 'allowed',
spdx_identifier: other_license_policy.software_license.spdx_identifier
)
end
end
context "when searching for policies by multiple classifications" do
let(:results) { subject.find_policies(classification: %w[allowed denied]) }
it 'includes an entry for each `allowed` and `denied` licensed' do
expect(results.count).to eq(2)
assert_matches(
results[0],
id: mit_policy.id,
name: mit_policy.software_license.name,
url: 'http://spdx.org/licenses/MIT.json',
classification: "denied",
spdx_identifier: mit_policy.software_license.spdx_identifier
)
assert_matches(
results[1],
id: other_license_policy.id,
name: other_license_policy.software_license.name,
url: nil,
classification: "allowed",
spdx_identifier: other_license_policy.software_license.spdx_identifier
)
end
end
context "when searching for detected policies matching a classification" do
let(:results) { subject.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
expect(results.count).to eq(1)
assert_matches(
results[0],
id: mit_policy.id,
name: mit_policy.software_license.name,
url: 'http://spdx.org/licenses/MIT.json',
classification: "denied",
spdx_identifier: mit_policy.software_license.spdx_identifier
)
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