diff options
| author | mo khan <mkhan@gitlab.com> | 2019-09-12 15:55:59 +0000 |
|---|---|---|
| committer | mo khan <mkhan@gitlab.com> | 2019-09-12 15:55:59 +0000 |
| commit | fa7a0dd6496b119b6f143ef8deba177b16f53d1c (patch) | |
| tree | f3f0738459d180a9ffa91c84c214ac75230774ff /lib | |
| parent | 8865a0bc643fdf4d3b679aff84e4f83ba2617936 (diff) | |
| parent | caa6ce0445aec8618924b41f7700173722e37d85 (diff) | |
Merge branch '13083-ruby-shim' into 'master'
Insert a shim to start generating v1 reports using the license finder Ruby API
See merge request gitlab-org/security-products/license-management!55
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/license/management.rb | 21 | ||||
| -rw-r--r-- | lib/license/management/loggable.rb | 15 | ||||
| -rw-r--r-- | lib/license/management/report.rb | 34 | ||||
| -rw-r--r-- | lib/license/management/report/base.rb | 38 | ||||
| -rw-r--r-- | lib/license/management/report/v1.rb | 84 | ||||
| -rw-r--r-- | lib/license/management/repository.rb | 49 | ||||
| -rw-r--r-- | lib/license/management/verifiable.rb | 15 | ||||
| -rw-r--r-- | lib/license/management/version.rb | 7 |
8 files changed, 263 insertions, 0 deletions
diff --git a/lib/license/management.rb b/lib/license/management.rb new file mode 100644 index 0000000..64fd4e8 --- /dev/null +++ b/lib/license/management.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'pathname' +require 'yaml' +require 'license_finder' +require 'license/management/loggable' +require 'license/management/verifiable' +require 'license/management/repository' +require 'license/management/report' +require 'license/management/version' + +# This applies a monkey patch to the JsonReport found in the `license_finder` gem. +LicenseFinder::JsonReport.prepend(License::Management::Report) + +module License + module Management + def self.root + Pathname.new(File.dirname(__FILE__)).join('../..') + end + end +end diff --git a/lib/license/management/loggable.rb b/lib/license/management/loggable.rb new file mode 100644 index 0000000..a44d45d --- /dev/null +++ b/lib/license/management/loggable.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module License + module Management + module Loggable + def logger + ::LicenseFinder::Core.default_logger + end + + def log_info(message) + logger.info(self.class, message) + end + end + end +end diff --git a/lib/license/management/report.rb b/lib/license/management/report.rb new file mode 100644 index 0000000..5ac41bb --- /dev/null +++ b/lib/license/management/report.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'license/management/report/base' +require 'license/management/report/v1' + +module License + module Management + module Report + DEFAULT_VERSION = '1' + VERSIONS = { + nil => V1, + '' => V1, + '1' => V1, + '1.0' => V1 + }.freeze + + # This method overrides the method defined in `LicenseFinder::JsonReport` to + # allow us to generate a custom json report. + def to_s + JSON.pretty_generate(version_for(report_version).to_h) + "\n" + end + + private + + def report_version + ENV.fetch('LM_REPORT_VERSION', DEFAULT_VERSION) + end + + def version_for(version) + VERSIONS.fetch(version.to_s).new(dependencies) + end + end + end +end diff --git a/lib/license/management/report/base.rb b/lib/license/management/report/base.rb new file mode 100644 index 0000000..ab9b6a1 --- /dev/null +++ b/lib/license/management/report/base.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module License + module Management + module Report + class Base + include Loggable + include Verifiable + + attr_reader :dependencies, :repository + + def initialize(dependencies) + @dependencies = dependencies + @repository = License::Management::Repository.new + end + + def to_h + raise NotImplementedError + end + + private + + def paths_from(dependency) + return [] unless dependency.respond_to?(:aggregate_paths) + + paths = dependency.aggregate_paths + return [] if blank?(paths) + + paths.map { |x| x.gsub(Dir.pwd, '.') } + end + + def description_for(dependency) + present?(dependency.summary) ? dependency.summary : dependency.description + end + end + end + end +end diff --git a/lib/license/management/report/v1.rb b/lib/license/management/report/v1.rb new file mode 100644 index 0000000..5882f72 --- /dev/null +++ b/lib/license/management/report/v1.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module License + module Management + module Report + class V1 < Base + def to_h + { + licenses: license_summary, + dependencies: formatted_dependencies(dependencies) + } + end + + private + + # when a dependency has multiple licenses, this will join the licenses into a single name + # this defect was backported from the html2json version of this script. + def license_summary + dependencies + .map { |dependency| join_license_names(dependency.licenses) } + .flatten + .group_by { |name| name } + .map { |license, items| { count: items.count, name: license } } + .sort_by { |hash| [-hash[:count], hash[:name]] } + end + + # when a dependency has more than one license + # this method chooses one of the urls. + # to maintain backwards compatibility this bug has been carried forward. + def license_for(dependency) + license = { name: join_license_names(dependency.licenses) } + + urls = dependency.licenses.map(&:url).reject { |x| blank?(x) }.uniq.sort + log_info("multiple urls detected: #{urls.inspect}") if urls.size > 1 + url = urls[0] || license_data(dependency.licenses.first)['url'] + + license[:url] = url if present?(url) + license + end + + def join_license_names(licenses) + licenses.map { |x| best_name_for(x) }.sort.reverse.join(', ') + end + + def map_from_dependency(dependency) + result = { + license: license_for(dependency), + dependency: { + name: dependency.name, + url: dependency.homepage, + description: description_for(dependency), + pathes: paths_from(dependency) + } + } + result[:dependency].delete(:url) if blank?(dependency.homepage) + result + end + + def formatted_dependencies(dependencies) + dependencies + .sort_by(&:name) + .map { |x| map_from_dependency(x) } + end + + def best_name_for(license) + license_data(license)['name'] + end + + def license_data(license) + return repository.item_for(license) if canonicalize? + + { + 'name' => license.name.split(/[\r\n]+/)[0], + 'url' => license.url + } + end + + def canonicalize? + ENV.fetch('LM_V1_CANONICALIZE', 'false') == 'true' + end + end + end + end +end diff --git a/lib/license/management/repository.rb b/lib/license/management/repository.rb new file mode 100644 index 0000000..2eee1b6 --- /dev/null +++ b/lib/license/management/repository.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module License + module Management + class Repository + include Loggable + include Verifiable + + def initialize( + compatibility_path: License::Management.root.join('normalized-licenses.yml') + ) + @compatibility_data = YAML.safe_load(IO.read(compatibility_path)) + end + + def item_for(license) + id = id_for(license) + item = id ? compatibility_data['licenses'][id] : nil + item ? { 'id' => id }.merge(item) : generate_item_for(license) + end + + private + + attr_reader :compatibility_data + + def id_for(license) + ids = compatibility_data['ids'] + ids[license.send(:short_name)] || ids[license.url] + end + + # When `license_finder` is unable to determine the license it will use the full + # content of the file as the name of the license. This method shrinks that name + # down to just take the first line of the file. + def take_first_line_from(content) + return '' if blank?(content) + + content.split(/[\r\n]+/)[0] + end + + def generate_item_for(license) + log_info("detected unknown license named `#{license.send(:short_name)}`") + { + 'id' => 'unknown', + 'name' => take_first_line_from(license.name), + 'url' => present?(license.url) ? license.url : '' + } + end + end + end +end diff --git a/lib/license/management/verifiable.rb b/lib/license/management/verifiable.rb new file mode 100644 index 0000000..72667fb --- /dev/null +++ b/lib/license/management/verifiable.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module License + module Management + module Verifiable + def blank?(item) + item.nil? || item.empty? + end + + def present?(item) + !blank?(item) + end + end + end +end diff --git a/lib/license/management/version.rb b/lib/license/management/version.rb new file mode 100644 index 0000000..7fec52d --- /dev/null +++ b/lib/license/management/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module License + module Management + VERSION = '1.7.0' + end +end |
