summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authormo khan <mkhan@gitlab.com>2019-09-12 15:55:59 +0000
committermo khan <mkhan@gitlab.com>2019-09-12 15:55:59 +0000
commitfa7a0dd6496b119b6f143ef8deba177b16f53d1c (patch)
treef3f0738459d180a9ffa91c84c214ac75230774ff /lib
parent8865a0bc643fdf4d3b679aff84e4f83ba2617936 (diff)
parentcaa6ce0445aec8618924b41f7700173722e37d85 (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.rb21
-rw-r--r--lib/license/management/loggable.rb15
-rw-r--r--lib/license/management/report.rb34
-rw-r--r--lib/license/management/report/base.rb38
-rw-r--r--lib/license/management/report/v1.rb84
-rw-r--r--lib/license/management/repository.rb49
-rw-r--r--lib/license/management/verifiable.rb15
-rw-r--r--lib/license/management/version.rb7
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