Commit 3e7cbd0f authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch '13083-parse-v2-license-report' into 'master'

Parse v2 license scanning reports

See merge request gitlab-org/gitlab!17646
parents 9f416afd 15c84f5e
---
title: Parse v2 license scanning reports
merge_request: 17646
author:
type: changed
......@@ -6,53 +6,21 @@ module Gitlab
module LicenseCompliance
class LicenseScanning
LicenseScanningParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
DEFAULT_VERSION = '1.0'
PARSERS = { '1' => V1, '2' => V2 }.freeze
def parse!(json_data, license_scanning_report)
root = JSON.parse(json_data)
def parse!(json_data, report)
json = JSON.parse(json_data, symbolize_names: true)
report.version = json[:version].presence || DEFAULT_VERSION
root['licenses'].each do |license_hash|
license_expression = license_hash['name']
# Extract licenses from the license_expression as it can contain comas.
each_license(license_expression) do |license_name|
license_dependencies = root['dependencies'].select do |dependency|
uses_license?(dependency['license']['name'], license_name)
end
license_dependencies.each do |dependency|
license_scanning_report.add_dependency(license_name,
license_hash['count'],
dependency['license']['url'],
dependency['dependency']['name'])
end
end
end
parser = PARSERS.fetch(report.major_version)
parser.new(report).parse(json)
rescue JSON::ParserError
raise LicenseScanningParserError, 'JSON parsing failed'
rescue => e
Gitlab::Sentry.track_exception(e)
raise LicenseScanningParserError, 'License scanning report parsing failed'
end
def remove_suffix(name)
name.gsub(/-or-later$|-only$|\+$/, '')
end
def expression_to_list(expression)
expression.split(',').map(&:strip).map { |name| remove_suffix(name) }
end
# Split the license expression when it is separated by spaces. Removes suffixes
# specified in https://spdx.org/ids-how
def each_license(expression)
expression_to_list(expression).each do |license_name|
yield(license_name)
end
end
# Check that the license expression uses the given license name
def uses_license?(expression, name)
expression_to_list(expression).any? { |name1| name1.casecmp(remove_suffix(name)) == 0 }
end
end
end
end
......
# frozen_string_literal: true
module Gitlab
module Ci
module Parsers
module LicenseCompliance
class V1
attr_reader :report
def initialize(report)
@report = report
end
def parse(json)
json.fetch(:dependencies, []).each do |dependency|
each_license_for(dependency) do |license_hash|
license = report.add_license(id: nil, name: license_hash[:name], url: license_hash[:url])
license.add_dependency(dependency[:dependency][:name])
end
end
end
private
def each_license_for(dependency)
if dependency.key?(:licenses)
dependency[:licenses].each do |license|
yield license
end
else
dependency[:license][:name].split(',').each do |name|
yield(name: remove_suffix(name.strip), url: dependency.dig(:license, :url))
end
end
end
def remove_suffix(name)
name.gsub(/-or-later$|-only$|\+$/, '')
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Ci
module Parsers
module LicenseCompliance
class V2
attr_reader :report
def initialize(report)
@report = report
end
def parse(report_hash)
add_licenses(report_hash)
add_dependencies(report_hash)
end
private
def add_licenses(report_hash)
report_hash[:licenses].map do |license_hash|
report.add_license(id: license_hash[:id], name: license_hash[:name], url: license_hash[:url])
end
end
def add_dependencies(report_hash)
report_hash[:dependencies].each do |dependency_hash|
dependency_hash[:licenses].map do |license_id|
license_for(license_id).add_dependency(dependency_hash[:name])
end
end
end
def license_for(license_id)
report.fetch(license_id) do |_key|
report.add_license(id: license_id, name: 'unknown')
end
end
end
end
end
end
end
......@@ -10,6 +10,14 @@ module Gitlab
def initialize(name)
@name = name
end
def hash
name.hash
end
def eql?(other)
self.name == other.name
end
end
end
end
......
......@@ -5,15 +5,25 @@ module Gitlab
module Reports
module LicenseScanning
class License
attr_reader :name, :url, :count
attr_reader :id, :name, :url
def initialize(name, count, url)
delegate :count, to: :dependencies
def initialize(id:, name:, url:)
@id = 'unknown' == id ? nil : id
@name = name
@count = count
@url = url
@dependencies = Set.new
end
def canonical_id
id || name&.downcase
end
def hash
canonical_id.hash
end
def add_dependency(name)
@dependencies.add(::Gitlab::Ci::Reports::LicenseScanning::Dependency.new(name))
end
......@@ -21,6 +31,12 @@ module Gitlab
def dependencies
@dependencies.to_a
end
def eql?(other)
super(other) ||
(id && other.id && id.eql?(other.id)) ||
(name && other.name && name.casecmp?(other.name))
end
end
end
end
......
......@@ -5,12 +5,18 @@ module Gitlab
module Reports
module LicenseScanning
class Report
attr_reader :found_licenses
delegate :empty?, :fetch, to: :found_licenses
attr_accessor :version
def initialize
def initialize(version: '1.0')
@version = version
@found_licenses = {}
end
def major_version
version.split('.')[0]
end
def licenses
found_licenses.values.sort_by { |license| license.name.downcase }
end
......@@ -19,10 +25,12 @@ module Gitlab
found_licenses.values.map(&:name)
end
def add_dependency(license_name, license_count, license_url, dependency_name)
key = license_name.upcase
found_licenses[key] ||= ::Gitlab::Ci::Reports::LicenseScanning::License.new(license_name, license_count, license_url)
found_licenses[key].add_dependency(dependency_name)
def add_license(id:, name:, url: '')
add(::Gitlab::Ci::Reports::LicenseScanning::License.new(id: id, name: name, url: url))
end
def add(license)
found_licenses[license.canonical_id] ||= license
end
def violates?(software_license_policies)
......@@ -30,25 +38,16 @@ module Gitlab
end
def diff_with(other_report)
base = self.license_names.map { |name| canonicalize(name) }
head = other_report.license_names.map { |name| canonicalize(name) }
base = self.licenses
head = other_report.licenses
{
added: other_report.find_by_names(head - base),
unchanged: find_by_names(base & head),
removed: find_by_names(base - head)
added: (head - base),
unchanged: (base & head),
removed: (base - head)
}
end
def find_by_names(names)
names = names.map { |name| canonicalize(name) }
licenses.select { |license| names.include?(canonicalize(license.name)) }
end
def empty?
found_licenses.empty?
end
def self.parse_from(json)
new.tap do |report|
::Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning.new.parse!(json, report)
......@@ -57,9 +56,7 @@ module Gitlab
private
def canonicalize(name)
name.downcase
end
attr_reader :found_licenses
end
end
end
......
......@@ -291,7 +291,7 @@ describe Projects::PipelinesController do
it 'will return mit license approved status' do
payload_mit = payload.find { |l| l['name'] == 'MIT' }
expect(payload_mit['count']).to eq(pipeline.license_scanning_report.found_licenses['MIT'].count)
expect(payload_mit['count']).to eq(pipeline.license_scanning_report.licenses.find { |x| x.name == 'MIT' }.count)
expect(payload_mit['url']).to eq('http://opensource.org/licenses/mit-license')
expect(payload_mit['classification']['approval_status']).to eq('approved')
end
......
# frozen_string_literal: true
FactoryBot.define do
factory :ci_reports_license_scanning_report, class: ::Gitlab::Ci::Reports::LicenseScanning::Report do
factory :ci_reports_license_scanning_report, class: ::Gitlab::Ci::Reports::LicenseScanning::Report, aliases: [:license_scan_report] do
trait :version_1 do
version { '1.0' }
end
trait :version_2 do
version { '2.0' }
end
trait :report_1 do
after(:build) do |report, evaluator|
report.add_dependency('MIT', 1, 'https://opensource.org/licenses/mit', 'Library1')
report.add_dependency('WTFPL', 1, 'https://opensource.org/licenses/wtfpl', 'Library2')
report.add_license(id: 'MIT', name: 'MIT', url: 'https://opensource.org/licenses/mit').add_dependency('Library1')
report.add_license(id: 'WTFPL', name: 'WTFPL', url: 'https://opensource.org/licenses/wtfpl').add_dependency('Library2')
end
end
trait :report_2 do
after(:build) do |report, evaluator|
report.add_dependency('MIT', 1, 'https://opensource.org/licenses/mit', 'Library1')
report.add_dependency('Apache 2.0', 1, 'https://opensource.org/licenses/apache', 'Library3')
report.add_license(id: 'MIT', name: 'MIT', url: 'https://opensource.org/licenses/mit').add_dependency('Library1')
report.add_license(id: 'Apache-2.0', name: 'Apache 2.0', url: 'https://opensource.org/licenses/apache').add_dependency('Library3')
end
end
trait :mit do
after(:build) do |report, evaluator|
report.add_dependency('MIT', 1, 'https://opensource.org/licenses/mit', 'rails')
report.add_license(id: 'MIT', name: 'MIT', url: 'https://opensource.org/licenses/mit').add_dependency('rails')
end
end
end
......
{
"version": "1.1",
"licenses": [
{
"count": 2,
"name": "MIT"
},
{
"count": 2,
"name": "BSD"
},
{
"count": 1,
"name": "unknown"
}
],
"dependencies": [
{
"licenses": [
{
"name": "MIT",
"url": "http://opensource.org/licenses/mit-license"
}
],
"license": {
"name": "MIT",
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
"name": "a",
"url": "https://example.org/a",
"description": "Dependency A.",
"pathes": [ "." ]
}
},
{
"licenses": [
{
"name": "BSD",
"url": "http://spdx.org/licenses/BSD-3-Clause.json"
}
],
"license": {
"name": "BSD",
"url": "http://spdx.org/licenses/BSD-3-Clause.json"
},
"dependency": {
"name": "b",
"url": "https://example.org/b",
"description": "Dependency B.",
"pathes": [ "." ]
}
},
{
"licenses": [
{
"name": "MIT",
"url": "http://opensource.org/licenses/mit-license"
},
{
"name": "BSD",
"url": "http://spdx.org/licenses/BSD-3-Clause.json"
}
],
"license": {
"name": "MIT, BSD",
"url": "http://opensource.org/licenses/mit-license"
},
"dependency": {
"name": "c",
"url": "https://example.org/c",
"description": "Dependency C.",
"pathes": [ "." ]
}
},
{
"licenses": [
{
"name": "unknown",
"url": ""
}
],
"license": {
"name": "unknown"
},
"dependency": {
"name": "d",
"url": "https://example.org/d",
"description": "Dependency D.",
"pathes": [ "." ]
}
}
]
}
{
"version": "2.0",
"licenses": [
{
"id": "BSD-3-Clause",
"name": "BSD 3-Clause \"New\" or \"Revised\" License",
"url": "http://spdx.org/licenses/BSD-3-Clause.json",
"count": 2
},
{
"id": "MIT",
"name": "MIT License",
"url": "http://spdx.org/licenses/MIT.json",
"count": 2
},
{
"id": "unknown",
"name": "unknown",
"url": "",
"count": 1
}
],
"dependencies": [
{
"name": "a",
"url": "https://example.org/a",
"description": "Dependency A.",
"paths": ["."],
"licenses": ["MIT"]
},
{
"name": "b",
"url": "https://example.com/b",
"description": "Dependency B.",
"paths": [ "." ],
"licenses": [ "BSD-3-Clause" ]
},
{
"name": "c",
"url": "https://example.org/c",
"description": "Dependency C.",
"paths": [ "." ],
"licenses": [ "MIT", "BSD-3-Clause" ]
},
{
"name": "d",
"url": "https://example.org/d",
"description": "Dependency D.",
"paths": [ "." ],
"licenses": [ "unknown" ]
}
]
}
......@@ -4,17 +4,169 @@ require 'spec_helper'
describe Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning do
describe '#parse!' do
subject { described_class.new.parse!(data, report) }
let(:report) { Gitlab::Ci::Reports::LicenseScanning::Report.new }
context 'when data is a JSON license management report' do
let(:data) { File.read(Rails.root.join('ee/spec/fixtures/security_reports/master/gl-license-management-report.json')) }
context 'when parsing a valid v1 report' do
let(:v1_json) { fixture_file('security_reports/master/gl-license-management-report.json', dir: 'ee') }
before do
subject.parse!(v1_json, report)
end
it { expect(report.version).to eql('1.0') }
it { expect(report.licenses.count).to eq(4) }
it { expect(report.licenses[0].name).to eql('Apache 2.0') }
it { expect(report.licenses[0].url).to eql('http://www.apache.org/licenses/LICENSE-2.0.txt') }
it { expect(report.licenses[0].count).to be(1) }
it { expect(report.licenses[0].dependencies.count).to be(1) }
it { expect(report.licenses[0].dependencies[0].name).to eql('thread_safe') }
it { expect(report.licenses[1].name).to eql('MIT') }
it { expect(report.licenses[1].url).to eql('http://opensource.org/licenses/mit-license') }
it { expect(report.licenses[1].count).to be(52) }
it { expect(report.licenses[1].dependencies.count).to be(52) }
it { expect(report.licenses[1].dependencies[0].name).to eql('actioncable') }
it { expect(report.licenses[2].name).to eql('New BSD') }
it { expect(report.licenses[2].url).to eql('http://opensource.org/licenses/BSD-3-Clause') }
it { expect(report.licenses[2].count).to be(3) }
it { expect(report.licenses[2].dependencies.count).to be(3) }
it { expect(report.licenses[2].dependencies.map(&:name)).to contain_exactly('ffi', 'puma', 'sqlite3') }
it { expect(report.licenses[3].name).to eql('unknown') }
it { expect(report.licenses[3].url).to be_nil }
it { expect(report.licenses[3].count).to be(1) }
it { expect(report.licenses[3].dependencies.count).to be(1) }
it { expect(report.licenses[3].dependencies[0].name).to eql('ruby-bundler-rails') }
end
context 'when parsing a valid v1.1 report' do
let(:v1_1_data) { fixture_file('security_reports/gl-license-management-report-v1.1.json', dir: 'ee') }
before do
subject.parse!(v1_1_data, report)
end
it { expect(report.version).to eql('1.1') }
it { expect(report.licenses.count).to eq(3) }
it { expect(report.licenses[0].id).to be_nil }
it { expect(report.licenses[0].name).to eql('BSD') }
it { expect(report.licenses[0].url).to eql('http://spdx.org/licenses/BSD-3-Clause.json') }
it { expect(report.licenses[0].count).to be(2) }
it { expect(report.licenses[0].dependencies.count).to be(2) }
it { expect(report.licenses[0].dependencies.map(&:name)).to contain_exactly('b', 'c') }
it { expect(report.licenses[1].id).to be_nil }
it { expect(report.licenses[1].name).to eql('MIT') }
it { expect(report.licenses[1].url).to eql('http://opensource.org/licenses/mit-license') }
it { expect(report.licenses[1].count).to be(2) }
it { expect(report.licenses[1].dependencies.count).to be(2) }
it { expect(report.licenses[1].dependencies.map(&:name)).to contain_exactly('a', 'c') }
it { expect(report.licenses[2].id).to be_nil }
it { expect(report.licenses[2].name).to eql('unknown') }
it { expect(report.licenses[2].url).to eql('') }
it { expect(report.licenses[2].count).to be(1) }
it { expect(report.licenses[2].dependencies.count).to be(1) }
it { expect(report.licenses[2].dependencies.map(&:name)).to contain_exactly('d') }
end
context 'when parsing a valid v2 report' do
let(:v2_data) { fixture_file('security_reports/gl-license-management-report-v2.json', dir: 'ee') }
before do
subject.parse!(v2_data, report)
end
it { expect(report.version).to eql('2.0') }
it { expect(report.licenses.count).to eq(3) }
it { expect(report.licenses[0].id).to eql('BSD-3-Clause') }
it { expect(report.licenses[0].name).to eql('BSD 3-Clause "New" or "Revised" License') }
it { expect(report.licenses[0].url).to eql('http://spdx.org/licenses/BSD-3-Clause.json') }
it { expect(report.licenses[0].count).to be(2) }
it { expect(report.licenses[0].dependencies.count).to be(2) }
it { expect(report.licenses[0].dependencies.map(&:name)).to contain_exactly('b', 'c') }
it { expect(report.licenses[1].id).to eql('MIT') }
it { expect(report.licenses[1].name).to eql('MIT License') }
it { expect(report.licenses[1].url).to eql('http://spdx.org/licenses/MIT.json') }
it { expect(report.licenses[1].count).to be(2) }
it { expect(report.licenses[1].dependencies.count).to be(2) }
it { expect(report.licenses[1].dependencies.map(&:name)).to contain_exactly('a', 'c') }
it 'parses without error' do
expect { subject }.not_to raise_error
it { expect(report.licenses[2].id).to be_nil }
it { expect(report.licenses[2].name).to eql('unknown') }
it { expect(report.licenses[2].url).to eql('') }
it { expect(report.licenses[2].count).to be(1) }
it { expect(report.licenses[2].dependencies.count).to be(1) }
it { expect(report.licenses[2].dependencies.map(&:name)).to contain_exactly('d') }
end
context 'when parsing a v2 report with a missing license definition' do
let(:v2_data) do
{
version: '2.0',
licenses: [],
dependencies: [
{ name: 'saml-kit', licenses: ['MIT'] }
]
}.to_json
end
before do
subject.parse!(v2_data, report)
end
it { expect(report.licenses.count).to be(1) }
it { expect(report.licenses[0].id).to eql('MIT') }
it { expect(report.licenses[0].name).to eql('unknown') }
it { expect(report.licenses[0].dependencies.count).to be(1) }
it { expect(report.licenses[0].dependencies[0].name).to eql('saml-kit') }
end
context 'when the report version is not recognized' do
it do
expect do
subject.parse!(JSON.pretty_generate({ version: 'x' }), report)
end.to raise_error(KeyError)
end
end
context 'when the report version is missing' do
before do
subject.parse!(JSON.pretty_generate({}), report)
end
it { expect(report.version).to eq('1.0') }
it { expect(report).to be_empty }
end
context 'when the report version is nil' do
before do
subject.parse!(JSON.pretty_generate({ version: nil }), report)
end
it { expect(report.version).to eq('1.0') }
it { expect(report).to be_empty }
end
context 'when the report version is blank' do
before do
subject.parse!(JSON.pretty_generate({ version: '' }), report)
end
it { expect(report.version).to eq('1.0') }
it { expect(report).to be_empty }
end
expect(report.licenses.count).to eq(4)
context 'when the report is not a valid JSON document' do
it do
expect do
subject.parse!('blah', report)
end.to raise_error(Gitlab::Ci::Parsers::LicenseCompliance::LicenseScanning::LicenseScanningParserError)
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Reports::LicenseScanning::Dependency do
describe 'value equality' do
let(:set) { Set.new }
it 'cannot add the same dependency to a set twice' do
set.add(described_class.new('bundler'))
set.add(described_class.new('bundler'))
expect(set.count).to eq(1)
end
it { expect(described_class.new('bundler')).to eql(described_class.new('bundler')) }
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Ci::Reports::LicenseScanning::License do
describe 'equality' do
let(:blank) { described_class.new(id: nil, name: nil, url: nil) }
let(:v1_mit) { described_class.new(id: nil, name: 'MIT', url: '') }
let(:v1_apache) { described_class.new(id: nil, name: 'Apache 2.0', url: '') }
let(:v2_mit) { described_class.new(id: 'MIT', name: 'MIT', url: '') }
let(:v2_apache) { described_class.new(id: 'Apache-2.0', name: 'Apache 2.0', url: '') }
describe '#eql?' do
it { expect([v1_mit, v1_apache] - [v2_mit, v2_apache]).to be_empty }
it { expect([v2_apache, v2_mit] & [v2_mit]).to match_array([v2_mit]) }
it { expect([v2_apache, v2_mit] - [v1_apache, v1_mit]).to be_empty }
it { expect([v2_apache] & [v2_mit]).to be_empty }
it { expect([v2_apache] - [v1_apache]).to be_empty }
it { expect([v2_apache] - [v1_mit]).to match_array([v2_apache]) }
it { expect(blank).not_to eql(v1_mit) }
it { expect(blank).not_to eql(v2_mit) }
it { expect(blank).to eql(blank) }
it { expect(v1_mit).not_to eql(blank) }
it { expect(v1_mit).not_to eql(v1_apache) }
it { expect(v1_mit).not_to eql(v2_apache) }
it { expect(v1_mit).to eql(v1_mit) }
it { expect(v1_mit).to eql(v2_mit) }
it { expect(v2_mit).not_to eql(blank) }
it { expect(v2_mit).not_to eql(v1_apache) }
it { expect(v2_mit).not_to eql(v2_apache) }
it { expect(v2_mit).to eql(v1_mit) }
it { expect(v2_mit).to eql(v2_mit) }
it { expect(v2_mit).to eql(described_class.new(id: v2_mit.id, name: '', url: '')) }
end
describe '#hash' do
it { expect(blank.hash).to eql(blank.dup.hash) }
it { expect(v1_mit.hash).to eql(v1_mit.dup.hash) }
it { expect(v2_mit.hash).to eql(v2_mit.dup.hash) }
end
end
describe '#canonical_id' do
context 'when the license was produced from a v1 report' do
subject { described_class.new(id: nil, name: 'MIT License', url: nil) }
it { expect(subject.canonical_id).to eql(subject.name.downcase) }
end
context 'when the license was produced from a v2 report' do
subject { described_class.new(id: 'MIT', name: 'MIT License', url: nil) }
it { expect(subject.canonical_id).to eql(subject.id) }
end
end
end
......@@ -10,17 +10,17 @@ describe Gitlab::Ci::Reports::LicenseScanning::Report do
let(:mit_license) { build(:software_license, :mit) }
let(:apache_license) { build(:software_license, :apache_2_0) }
context "when a blacklisted license is found in the report" do
context 'when a blacklisted license is found in the report' do
let(:mit_blacklist) { build(:software_license_policy, :blacklist, software_license: mit_license) }
before do
project.software_license_policies << mit_blacklist
end
specify { expect(subject.violates?(project.software_license_policies)).to be(true) }
it { expect(subject.violates?(project.software_license_policies)).to be(true) }
end
context "when a blacklisted license is discovered with a different casing for the name" do
context 'when a blacklisted license is discovered with a different casing for the name' do
let(:mit_blacklist) { build(:software_license_policy, :blacklist, software_license: mit_license) }
before do
......@@ -28,32 +28,58 @@ describe Gitlab::Ci::Reports::LicenseScanning::Report do
project.software_license_policies << mit_blacklist
end
specify { expect(subject.violates?(project.software_license_policies)).to be(true) }
it { expect(subject.violates?(project.software_license_policies)).to be(true) }
end
context "when none of the licenses discovered in the report violate the blacklist policy" do
context 'when none of the licenses discovered in the report violate the blacklist policy' do
let(:apache_blacklist) { build(:software_license_policy, :blacklist, software_license: apache_license) }
before do
project.software_license_policies << apache_blacklist
end
specify { expect(subject.violates?(project.software_license_policies)).to be(false) }
it { expect(subject.violates?(project.software_license_policies)).to be(false) }
end
end
describe "#diff_with" do
let(:report_1) { build(:ci_reports_license_scanning_report, :report_1) }
let(:report_2) { build(:ci_reports_license_scanning_report, :report_2) }
subject { report_1.diff_with(report_2) }
describe '#diff_with' do
def names_from(licenses)
licenses.map(&:name)
end
context 'when diffing two v1 reports' do
let(:base_report) { build(:license_scan_report, :version_1) }
let(:head_report) { build(:license_scan_report, :version_1) }
subject { base_report.diff_with(head_report) }
before do
report_1.add_dependency('BSD', 1, 'https://opensource.org/licenses/0BSD', 'Library1')
report_2.add_dependency('bsd', 1, 'https://opensource.org/licenses/0BSD', 'Library1')
base_report.add_license(id: nil, name: 'MIT').add_dependency('Library1')
base_report.add_license(id: nil, name: 'BSD').add_dependency('Library1')
base_report.add_license(id: nil, name: 'WTFPL').add_dependency('Library2')
head_report.add_license(id: nil, name: 'MIT').add_dependency('Library1')
head_report.add_license(id: nil, name: 'Apache 2.0').add_dependency('Library3')
head_report.add_license(id: nil, name: 'bsd').add_dependency('Library1')
end
def names_from(licenses)
licenses.map(&:name)
it { expect(names_from(subject[:added])).to contain_exactly('Apache 2.0') }
it { expect(names_from(subject[:unchanged])).to contain_exactly('MIT', 'BSD') }
it { expect(names_from(subject[:removed])).to contain_exactly('WTFPL') }
end
context 'when diffing two v2 reports' do
let(:base_report) { build(:license_scan_report, :version_2) }
let(:head_report) { build(:license_scan_report, :version_2) }
subject { base_report.diff_with(head_report) }
before do
base_report.add_license(id: 'MIT', name: 'MIT').add_dependency('Library1')
base_report.add_license(id: 'BSD-3-Clause', name: 'BSD').add_dependency('Library1')
base_report.add_license(id: 'WTFPL', name: 'WTFPL').add_dependency('Library2')
head_report.add_license(id: 'BSD-3-Clause', name: 'bsd').add_dependency('Library1')
head_report.add_license(id: 'Apache-2.0', name: 'Apache 2.0').add_dependency('Library3')
head_report.add_license(id: 'MIT', name: 'MIT License').add_dependency('Library1')
end
it { expect(names_from(subject[:added])).to contain_exactly('Apache 2.0') }
......@@ -61,7 +87,48 @@ describe Gitlab::Ci::Reports::LicenseScanning::Report do
it { expect(names_from(subject[:removed])).to contain_exactly('WTFPL') }
end
describe "#empty?" do
context 'when diffing a v1 report with a v2 report' do
let(:base_report) { build(:license_scan_report, :version_1) }
let(:head_report) { build(:license_scan_report, :version_2) }
subject { base_report.diff_with(head_report) }
before do
base_report.add_license(id: nil, name: 'MIT').add_dependency('Library1')
base_report.add_license(id: nil, name: 'BSD').add_dependency('Library1')
base_report.add_license(id: nil, name: 'WTFPL').add_dependency('Library2')
head_report.add_license(id: 'BSD-3-Clause', name: 'bsd').add_dependency('Library1')
head_report.add_license(id: 'Apache-2.0', name: 'Apache 2.0').add_dependency('Library3')
head_report.add_license(id: 'MIT', name: 'MIT').add_dependency('Library1')
end
it { expect(names_from(subject[:added])).to contain_exactly('Apache 2.0') }
it { expect(names_from(subject[:unchanged])).to contain_exactly('MIT', 'BSD') }
it { expect(names_from(subject[:removed])).to contain_exactly('WTFPL') }
end
context 'when diffing a v2 report with a v1 report' do
let(:base_report) { build(:license_scan_report, :version_2) }
let(:head_report) { build(:license_scan_report, :version_1) }
subject { base_report.diff_with(head_report) }
before do
base_report.add_license(id: 'MIT', name: 'MIT').add_dependency('Library1')
base_report.add_license(id: 'BSD-3-Clause', name: 'BSD').add_dependency('Library1')
base_report.add_license(id: 'WTFPL', name: 'WTFPL').add_dependency('Library2')
head_report.add_license(id: nil, name: 'bsd').add_dependency('Library1')
head_report.add_license(id: nil, name: 'Apache 2.0').add_dependency('Library3')
head_report.add_license(id: nil, name: 'MIT').add_dependency('Library1')
end
it { expect(names_from(subject[:added])).to contain_exactly('Apache 2.0') }
it { expect(names_from(subject[:unchanged])).to contain_exactly('MIT', 'BSD') }
it { expect(names_from(subject[:removed])).to contain_exactly('WTFPL') }
end
end
describe '#empty?' do
let(:completed_report) { build(:ci_reports_license_scanning_report, :report_1) }
let(:empty_report) { build(:ci_reports_license_scanning_report) }
......@@ -69,12 +136,21 @@ describe Gitlab::Ci::Reports::LicenseScanning::Report do
it { expect(completed_report).not_to be_empty }
end
describe ".parse_from" do
context "when parsing a v1 report" do
describe '.parse_from' do
context 'when parsing a v1 report' do
subject { described_class.parse_from(v1_json) }
let(:v1_json) { fixture_file('security_reports/master/gl-license-management-report.json', dir: 'ee') }
specify { expect(subject.licenses.count).to eq(4) }
it { expect(subject.version).to eql('1.0') }
it { expect(subject.licenses.count).to eq(4) }
end
context 'when parsing a v2 report' do
subject { described_class.parse_from(v2_json) }
let(:v2_json) { fixture_file('security_reports/gl-license-management-report-v2.json', dir: 'ee') }
it { expect(subject.version).to eql('2.0') }
it { expect(subject.licenses.count).to eq(3) }
end
end
end
......@@ -8,8 +8,8 @@ describe Gitlab::Ci::Reports::LicenseScanning::ReportsComparer do
let(:report_comparer) { described_class.new(report_1, report_2) }
before do
report_1.add_dependency('BSD', 1, 'https://opensource.org/licenses/0BSD', 'Library1')
report_2.add_dependency('bsd', 1, 'https://opensource.org/licenses/0BSD', 'Library1')
report_1.add_license(id: nil, name: 'BSD').add_dependency('Library1')
report_2.add_license(id: nil, name: 'bsd').add_dependency('Library1')
end
def names_from(licenses)
......
......@@ -191,8 +191,8 @@ describe Ci::Build do
expect { subject }.not_to raise_error
expect(license_scanning_report.licenses.count).to eq(4)
expect(license_scanning_report.found_licenses['MIT'].name).to eq('MIT')
expect(license_scanning_report.found_licenses['MIT'].dependencies.count).to eq(52)
expect(license_scanning_report.licenses.map(&:name)).to contain_exactly("Apache 2.0", "MIT", "New BSD", "unknown")
expect(license_scanning_report.licenses.find { |x| x.name == 'MIT' }.dependencies.count).to eq(52)
end
end
......
......@@ -5,7 +5,7 @@ module LicenseScanningReportHelper
Gitlab::Ci::Reports::LicenseScanning::Report.new.tap do |report|
dependencies.each do |license_name, dependencies|
dependencies.each do |dependency_name|
report.add_dependency(license_name.to_s, 1, "https://opensource.org/licenses/license1", dependency_name)
report.add_license(id: nil, name: license_name.to_s, url: "https://opensource.org/licenses/license1").add_dependency(dependency_name)
end
end
end
......@@ -32,7 +32,7 @@ module LicenseScanningReportHelper
end
def create_license
Gitlab::Ci::Reports::LicenseScanning::License.new('License1', 1, "https://opensource.org/licenses/license1").tap do |license|
Gitlab::Ci::Reports::LicenseScanning::License.new(id: nil, name: 'License1', url: "https://opensource.org/licenses/license1").tap do |license|
license.add_dependency('Dependency1')
license.add_dependency('Dependency2')
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