license_finder 6.1.2 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/CHANGELOG.md +64 -0
  4. data/Dockerfile +23 -15
  5. data/README.md +24 -2
  6. data/Rakefile +1 -1
  7. data/VERSION +1 -1
  8. data/ci/pipelines/release.yml.erb +8 -19
  9. data/ci/scripts/pushscript.sh +1 -1
  10. data/ci/scripts/updateChangelog.sh +8 -1
  11. data/ci/tasks/build-and-push-gem.yml +2 -2
  12. data/ci/tasks/rubocop.yml +1 -1
  13. data/ci/tasks/update-changelog.yml +2 -2
  14. data/lib/license_finder/cli.rb +1 -0
  15. data/lib/license_finder/cli/base.rb +1 -0
  16. data/lib/license_finder/cli/inherited_decisions.rb +32 -0
  17. data/lib/license_finder/cli/main.rb +3 -1
  18. data/lib/license_finder/configuration.rb +4 -0
  19. data/lib/license_finder/decision_applier.rb +8 -4
  20. data/lib/license_finder/decisions.rb +63 -20
  21. data/lib/license_finder/license/definitions.rb +48 -1
  22. data/lib/license_finder/license/templates/0BSD.txt +10 -0
  23. data/lib/license_finder/license/templates/OFL.txt +91 -0
  24. data/lib/license_finder/license/templates/SimplifiedBSD.txt +0 -4
  25. data/lib/license_finder/license/templates/WTFPL.txt +14 -0
  26. data/lib/license_finder/license/text.rb +24 -2
  27. data/lib/license_finder/logger.rb +2 -0
  28. data/lib/license_finder/package.rb +2 -1
  29. data/lib/license_finder/package_manager.rb +6 -2
  30. data/lib/license_finder/package_managers/bundler.rb +5 -3
  31. data/lib/license_finder/package_managers/cargo.rb +2 -1
  32. data/lib/license_finder/package_managers/composer.rb +5 -1
  33. data/lib/license_finder/package_managers/dep.rb +2 -2
  34. data/lib/license_finder/package_managers/dotnet.rb +2 -1
  35. data/lib/license_finder/package_managers/glide.rb +2 -7
  36. data/lib/license_finder/package_managers/go_15vendorexperiment.rb +1 -1
  37. data/lib/license_finder/package_managers/go_modules.rb +11 -4
  38. data/lib/license_finder/package_managers/go_workspace.rb +5 -1
  39. data/lib/license_finder/package_managers/nuget.rb +37 -3
  40. data/lib/license_finder/package_managers/pipenv.rb +1 -1
  41. data/lib/license_finder/package_managers/sbt.rb +3 -1
  42. data/lib/license_finder/package_managers/yarn.rb +16 -2
  43. data/lib/license_finder/package_utils/license_files.rb +2 -2
  44. data/lib/license_finder/packages/bower_package.rb +7 -0
  45. data/lib/license_finder/packages/bundler_package.rb +4 -0
  46. data/lib/license_finder/packages/cargo_package.rb +4 -0
  47. data/lib/license_finder/packages/cocoa_pods_package.rb +4 -0
  48. data/lib/license_finder/packages/composer_package.rb +4 -0
  49. data/lib/license_finder/packages/conan_package.rb +4 -0
  50. data/lib/license_finder/packages/go_package.rb +5 -1
  51. data/lib/license_finder/packages/gradle_package.rb +4 -0
  52. data/lib/license_finder/packages/maven_package.rb +6 -1
  53. data/lib/license_finder/packages/merged_package.rb +1 -1
  54. data/lib/license_finder/packages/mix_package.rb +4 -0
  55. data/lib/license_finder/packages/npm_package.rb +4 -0
  56. data/lib/license_finder/packages/nuget_package.rb +4 -0
  57. data/lib/license_finder/packages/pip_package.rb +4 -0
  58. data/lib/license_finder/packages/rebar_package.rb +4 -0
  59. data/lib/license_finder/packages/yarn_package.rb +4 -0
  60. data/lib/license_finder/reports/csv_report.rb +7 -3
  61. data/lib/license_finder/reports/json_report.rb +2 -0
  62. data/license_finder.gemspec +5 -5
  63. metadata +20 -22
@@ -6,15 +6,37 @@ module LicenseFinder
6
6
  SPACES = /\s+/.freeze
7
7
  QUOTES = /['`"]{1,2}/.freeze
8
8
  PLACEHOLDERS = /<[^<>]+>/.freeze
9
+ SPECIAL_SINGLE_QUOTES = /[‘’]/.freeze
10
+ SPECIAL_DOUBLE_QUOTES = /[“”„«»]/.freeze
11
+ ALPHABET_ORDERED_LIST = /\\\([a-z]\\\)\\\s/.freeze
12
+ ALPHABET_ORDERED_LIST_OPTIONAL = '(\([a-z]\)\s)?'
13
+ LIST_BULLETS = /(\d{1,2}\\\.|\\\*)\\\s/.freeze
14
+ LIST_BULLETS_OPTIONAL = '(\d{1,2}.|\*)?\s*'
15
+ NEWLINE_CHARACTER = /\n+/.freeze
16
+ QUOTE_COMMENT_CHARACTER = /^\s*\>+/.freeze
17
+ ESCAPED_QUOTES = /\\\"/.freeze
9
18
 
10
19
  def self.normalize_punctuation(text)
11
- text.gsub(SPACES, ' ')
20
+ text.dup.force_encoding('UTF-8')
21
+ .gsub(SPECIAL_DOUBLE_QUOTES, '"')
22
+ .gsub(SPECIAL_SINGLE_QUOTES, "'")
23
+ .gsub(QUOTE_COMMENT_CHARACTER, '')
24
+ .gsub(SPACES, ' ')
25
+ .gsub(NEWLINE_CHARACTER, ' ')
26
+ .gsub(ESCAPED_QUOTES, '"')
12
27
  .gsub(QUOTES, '"')
13
28
  .strip
29
+ rescue ArgumentError => _e
30
+ text
14
31
  end
15
32
 
16
33
  def self.compile_to_regex(text)
17
- Regexp.new(Regexp.escape(text).gsub(PLACEHOLDERS, '(.*)'))
34
+ Regexp.new(Regexp.escape(normalize_punctuation(text))
35
+ .gsub(PLACEHOLDERS, '(.*)')
36
+ .gsub(',', '(,)?')
37
+ .gsub('HOLDER', '(HOLDER|OWNER)')
38
+ .gsub(ALPHABET_ORDERED_LIST, ALPHABET_ORDERED_LIST_OPTIONAL)
39
+ .gsub(LIST_BULLETS, LIST_BULLETS_OPTIONAL))
18
40
  end
19
41
  end
20
42
  end
@@ -36,6 +36,8 @@ module LicenseFinder
36
36
  "\e[31m#{string}\e[0m"
37
37
  when :green
38
38
  "\e[32m#{string}\e[0m"
39
+ when :magenta
40
+ "\e[35m#{string}\e[0m"
39
41
  else
40
42
  string
41
43
  end
@@ -43,6 +43,7 @@ module LicenseFinder
43
43
  @summary = options[:summary] || ''
44
44
  @description = options[:description] || ''
45
45
  @homepage = options[:homepage] || ''
46
+ @package_url = options[:package_url].to_s
46
47
  @children = options[:children] || []
47
48
  @parents = Set.new # will be figured out later by package manager
48
49
  @groups = options[:groups] || []
@@ -61,7 +62,7 @@ module LicenseFinder
61
62
 
62
63
  ## DESCRIPTION
63
64
 
64
- attr_accessor :homepage
65
+ attr_accessor :homepage, :package_url
65
66
 
66
67
  attr_reader :name, :version, :authors,
67
68
  :summary, :description,
@@ -119,8 +119,12 @@ module LicenseFinder
119
119
  attr_reader :logger, :project_path
120
120
 
121
121
  def log_errors(stderr)
122
- logger.info prepare_command, 'did not succeed.', color: :red
123
- logger.info prepare_command, stderr, color: :red
122
+ log_errors_with_cmd(prepare_command, stderr)
123
+ end
124
+
125
+ def log_errors_with_cmd(prep_cmd, stderr)
126
+ logger.info prep_cmd, 'did not succeed.', color: :red
127
+ logger.info prep_cmd, stderr, color: :red
124
128
  log_to_file stderr
125
129
  end
126
130
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler'
4
+ require 'securerandom'
4
5
 
5
6
  module LicenseFinder
6
7
  class Bundler < PackageManager
@@ -26,7 +27,10 @@ module LicenseFinder
26
27
  def prepare_command
27
28
  ignored_groups_argument = !ignored_groups.empty? ? "--without #{ignored_groups.to_a.join(' ')}" : ''
28
29
 
29
- "bundle install #{ignored_groups_argument}".strip
30
+ gem_path = "lf-bundler-gems-#{SecureRandom.uuid}"
31
+ logger.info self.class, "Running bundle install for #{Dir.pwd} with path #{gem_path}", color: :blue
32
+
33
+ "bundle install #{ignored_groups_argument} --path #{gem_path}".strip
30
34
  end
31
35
 
32
36
  def possible_package_paths
@@ -38,8 +42,6 @@ module LicenseFinder
38
42
  attr_reader :ignored_groups
39
43
 
40
44
  def definition
41
- # DI
42
- ENV['BUNDLE_PATH'] = project_path.to_s
43
45
  ENV['BUNDLE_GEMFILE'] = "#{project_path}/#{gemfile}"
44
46
 
45
47
  @definition ||= ::Bundler::Definition.build(detected_package_path, lockfile_path, nil)
@@ -6,7 +6,8 @@ module LicenseFinder
6
6
  class Cargo < PackageManager
7
7
  def current_packages
8
8
  cargo_output.map do |package|
9
- CargoPackage.new(package, logger: logger)
9
+ path = Dir.glob("#{Dir.home}/.cargo/registry/src/**/#{package['name']}-#{package['version']}").first
10
+ CargoPackage.new(package, logger: logger, install_path: path)
10
11
  end
11
12
  end
12
13
 
@@ -12,7 +12,11 @@ module LicenseFinder
12
12
 
13
13
  def current_packages
14
14
  dependency_list.map do |name, dependency|
15
- ComposerPackage.new(name, dependency['version'], spec_licenses: dependency['license'])
15
+ path_command = "composer show #{name} -P"
16
+ stdout, _stderr, status = Dir.chdir(project_path) { Cmd.run(path_command) }
17
+
18
+ path = status.success? ? stdout.split(' ').last : ''
19
+ ComposerPackage.new(name, dependency['version'], spec_licenses: dependency['license'], install_path: path)
16
20
  end
17
21
  end
18
22
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'toml'
3
+ require 'tomlrb'
4
4
 
5
5
  module LicenseFinder
6
6
  class Dep < PackageManager
@@ -9,7 +9,7 @@ module LicenseFinder
9
9
  end
10
10
 
11
11
  def current_packages
12
- toml = TOML.load_file(detected_package_path)
12
+ toml = Tomlrb.load_file(detected_package_path)
13
13
  projects = toml['projects']
14
14
 
15
15
  return [] if projects.nil?
@@ -63,7 +63,8 @@ module LicenseFinder
63
63
  .uniq { |d| [d.name, d.version] }
64
64
 
65
65
  package_metadatas.map do |d|
66
- NugetPackage.new(d.name, d.version, spec_licenses: d.read_license_urls)
66
+ path = Dir.glob("#{Dir.home}/.nuget/packages/#{d.name.downcase}/#{d.version}").first
67
+ NugetPackage.new(d.name, d.version, spec_licenses: d.read_license_urls, install_path: path)
67
68
  end
68
69
  end
69
70
 
@@ -3,7 +3,7 @@
3
3
  module LicenseFinder
4
4
  class Glide < PackageManager
5
5
  def possible_package_paths
6
- [project_path.join('src', 'glide.lock'), project_path.join('glide.lock')]
6
+ [project_path.join('glide.lock')]
7
7
  end
8
8
 
9
9
  def current_packages
@@ -11,12 +11,7 @@ module LicenseFinder
11
11
 
12
12
  YAML.load_file(detected_path).fetch('imports').map do |package_hash|
13
13
  import_path = package_hash.fetch('name')
14
- license_path =
15
- if detected_path == possible_package_paths.first
16
- project_path.join('src', 'vendor', import_path)
17
- else
18
- project_path.join('vendor', import_path)
19
- end
14
+ license_path = project_path.join('vendor', import_path)
20
15
 
21
16
  GoPackage.from_dependency({
22
17
  'ImportPath' => import_path,
@@ -14,7 +14,7 @@ module LicenseFinder
14
14
  end
15
15
 
16
16
  def go_files_exist?
17
- !Dir[project_path.join('**/*.go')].empty?
17
+ !Dir[project_path.join('**/*.go')].empty? && !Dir[project_path.join('vendor/**/*.go')].empty?
18
18
  end
19
19
 
20
20
  def possible_package_paths
@@ -21,12 +21,10 @@ module LicenseFinder
21
21
  end
22
22
 
23
23
  def current_packages
24
- info_output, _stderr, _status = Cmd.run("GO111MODULE=on go list -m -mod=vendor -f '{{.Path}},{{.Version}},{{.Dir}}' all")
25
- packages_info = info_output.split("\n")
26
24
  packages = packages_info.map do |package|
27
25
  name, version, install_path = package.split(',')
28
- read_package(install_path, name, version)
29
- end
26
+ read_package(install_path, name, version) if install_path.to_s != ''
27
+ end.compact
30
28
  packages.reject do |package|
31
29
  Pathname(package.install_path).cleanpath == Pathname(project_path).cleanpath
32
30
  end
@@ -34,6 +32,15 @@ module LicenseFinder
34
32
 
35
33
  private
36
34
 
35
+ def packages_info
36
+ info_output, stderr, _status = Cmd.run("GO111MODULE=on go list -m -f '{{.Path}},{{.Version}},{{.Dir}}' all")
37
+ if stderr =~ Regexp.compile("can't compute 'all' using the vendor directory")
38
+ info_output, _stderr, _status = Cmd.run("GO111MODULE=on go list -m -mod=mod -f '{{.Path}},{{.Version}},{{.Dir}}' all")
39
+ end
40
+
41
+ info_output.split("\n")
42
+ end
43
+
37
44
  def sum_files?
38
45
  sum_file_paths.any?
39
46
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+
4
5
  module LicenseFinder
5
6
  class GoWorkspacePackageManagerError < ::StandardError
6
7
  end
@@ -51,9 +52,10 @@ module LicenseFinder
51
52
  return false if @strict_matching
52
53
 
53
54
  godep = LicenseFinder::GoDep.new(project_path: Pathname(project_path))
55
+ dep = LicenseFinder::Dep.new(project_path: Pathname(project_path))
54
56
  # go workspace is only active if GoDep wasn't. There are some projects
55
57
  # that will use the .envrc and have a Godep folder as well.
56
- !!(!godep.active? && envrc_path && ENVRC_REGEXP.match(IO.read(envrc_path)))
58
+ !!(!godep.active? && !dep.active? && envrc_path && ENVRC_REGEXP.match(IO.read(envrc_path)))
57
59
  end
58
60
 
59
61
  private
@@ -81,6 +83,8 @@ module LicenseFinder
81
83
  orig_gopath = ENV['GOPATH']
82
84
  ENV['GOPATH'] = nil
83
85
  val, stderr, status = Cmd.run('go list -f "{{join .Deps \"\n\"}}" ./...')
86
+ ENV['GOPATH'] = project_path.to_s
87
+ val, stderr, status = Cmd.run('go list -f "{{join .Deps \"\n\"}}" ./...') unless status.success?
84
88
  ENV['GOPATH'] = orig_gopath
85
89
  raise GoWorkspacePackageManagerError, "go list failed:\n#{stderr}" unless status.success?
86
90
 
@@ -51,7 +51,9 @@ module LicenseFinder
51
51
  def current_packages
52
52
  dependencies.each_with_object({}) do |dep, memo|
53
53
  licenses = license_urls(dep)
54
- memo[dep.name] ||= NugetPackage.new(dep.name, dep.version, spec_licenses: licenses)
54
+ path = Dir.glob("#{Dir.home}/.nuget/packages/#{dep.name.downcase}/#{dep.version}").first
55
+
56
+ memo[dep.name] ||= NugetPackage.new(dep.name, dep.version, spec_licenses: licenses, install_path: path)
55
57
  memo[dep.name].groups << dep.assembly unless memo[dep.name].groups.include? dep.assembly
56
58
  end.values
57
59
  end
@@ -71,10 +73,42 @@ module LicenseFinder
71
73
  assemblies.flat_map(&:dependencies)
72
74
  end
73
75
 
76
+ def nuget_binary
77
+ legacy_vcproj = Dir['**/*.vcproj'].any?
78
+
79
+ if legacy_vcproj
80
+ '/usr/local/bin/nugetv3.5.0.exe'
81
+ else
82
+ '/usr/local/bin/nuget.exe'
83
+ end
84
+ end
85
+
74
86
  def package_management_command
75
87
  return 'nuget' if LicenseFinder::Platform.windows?
76
88
 
77
- 'mono /usr/local/bin/nuget.exe'
89
+ "mono #{nuget_binary}"
90
+ end
91
+
92
+ def prepare
93
+ cmd = prepare_command
94
+ stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(cmd) }
95
+ return if status.success?
96
+
97
+ log_errors stderr
98
+
99
+ if stderr.include?('-PackagesDirectory')
100
+ logger.info cmd, 'trying fallback prepare command', color: :magenta
101
+
102
+ cmd = "#{cmd} -PackagesDirectory ."
103
+ stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(cmd) }
104
+ return if status.success?
105
+
106
+ log_errors_with_cmd(cmd, stderr)
107
+ end
108
+
109
+ error_message = "Prepare command '#{cmd}' failed\n#{stderr}"
110
+ error_message += "\n#{stdout}\n" if !stdout.nil? && !stdout.empty?
111
+ raise error_message unless @prepare_no_fail
78
112
  end
79
113
 
80
114
  def prepare_command
@@ -94,7 +128,7 @@ module LicenseFinder
94
128
  def nuget_check
95
129
  return 'where nuget' if LicenseFinder::Platform.windows?
96
130
 
97
- 'which mono && ls /usr/local/bin/nuget.exe'
131
+ "which mono && ls #{nuget_binary}"
98
132
  end
99
133
 
100
134
  def self.nuspec_license_urls(specfile_content)
@@ -15,7 +15,7 @@ module LicenseFinder
15
15
  begin
16
16
  packages = {}
17
17
  each_dependency(groups: allowed_groups) do |name, data, group|
18
- version = canonicalize(data['version'])
18
+ version = canonicalize(data['version'] || 'unknown')
19
19
  package = packages.fetch(key_for(name, version)) do |key|
20
20
  packages[key] = build_package_for(name, version)
21
21
  end
@@ -30,7 +30,9 @@ module LicenseFinder
30
30
  'version' => version,
31
31
  'licenses' => [{ 'name' => row['License'] }]
32
32
  }
33
- SbtPackage.new(spec, logger: logger, include_groups: @include_groups)
33
+
34
+ path = File.join("#{Dir.home}/.ivy2/cache", "#{spec['groupId']}/#{spec['artifactId']}")
35
+ SbtPackage.new(spec, logger: logger, include_groups: @include_groups, install_path: path)
34
36
  end
35
37
  end
36
38
 
@@ -72,11 +72,25 @@ module LicenseFinder
72
72
  valid_packages = filter_yarn_internal_package(packages)
73
73
 
74
74
  valid_packages.map do |package_hash|
75
- YarnPackage.new(package_hash['Name'], package_hash['Version'], spec_licenses: [package_hash['License']],
76
- homepage: package_hash['VendorUrl'])
75
+ YarnPackage.new(
76
+ package_hash['Name'],
77
+ package_hash['Version'],
78
+ spec_licenses: [package_hash['License']],
79
+ homepage: package_hash['VendorUrl'],
80
+ authors: package_hash['VendorName'],
81
+ install_path: project_path.join(modules_folder, package_hash['Name'])
82
+ )
77
83
  end
78
84
  end
79
85
 
86
+ def modules_folder
87
+ return @modules_folder if @modules_folder
88
+
89
+ stdout, _stderr, status = Cmd.run('yarn config get modules-folder')
90
+ @modules_folder = 'node_modules' if !status.success? || stdout.strip == 'undefined'
91
+ @modules_folder ||= stdout.strip
92
+ end
93
+
80
94
  # remove fake package created by yarn [Yarn Bug]
81
95
  def filter_yarn_internal_package(all_packages)
82
96
  internal_package_pattern = /workspace-aggregator-[a-zA-z0-9]{8}-[a-zA-z0-9]{4}-[a-zA-z0-9]{4}-[a-zA-z0-9]{4}-[a-zA-z0-9]{12}/
@@ -4,7 +4,7 @@ require 'license_finder/package_utils/possible_license_file'
4
4
 
5
5
  module LicenseFinder
6
6
  class LicenseFiles
7
- CANDIDATE_FILE_NAMES = %w[LICENSE License LICENCE Licence COPYING README Readme ReadMe].freeze
7
+ CANDIDATE_FILE_NAMES = %w[License Licence COPYING README].freeze
8
8
  CANDIDATE_PATH_WILDCARD = "*{#{CANDIDATE_FILE_NAMES.join(',')}}*"
9
9
 
10
10
  def self.find(install_path, options = {})
@@ -35,7 +35,7 @@ module LicenseFinder
35
35
  def candidate_files_and_dirs
36
36
  return [] if install_path.nil?
37
37
 
38
- Pathname.glob(install_path.join('**', CANDIDATE_PATH_WILDCARD))
38
+ Pathname.glob(install_path.join('**', CANDIDATE_PATH_WILDCARD), File::FNM_CASEFOLD)
39
39
  end
40
40
  end
41
41
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open-uri'
4
+
3
5
  module LicenseFinder
4
6
  class BowerPackage < Package
5
7
  def initialize(bower_module, options = {})
@@ -31,5 +33,10 @@ module LicenseFinder
31
33
  def package_manager
32
34
  'Bower'
33
35
  end
36
+
37
+ def package_url
38
+ meta = JSON.parse(open("https://registry.bower.io/packages/#{CGI.escape(name)}").read)
39
+ meta['url']
40
+ end
34
41
  end
35
42
  end
@@ -25,5 +25,9 @@ module LicenseFinder
25
25
  def package_manager
26
26
  'Bundler'
27
27
  end
28
+
29
+ def package_url
30
+ "https://rubygems.org/gems/#{CGI.escape(name)}/versions/#{CGI.escape(version)}"
31
+ end
28
32
  end
29
33
  end
@@ -20,5 +20,9 @@ module LicenseFinder
20
20
  def package_manager
21
21
  'Cargo'
22
22
  end
23
+
24
+ def package_url
25
+ "https://crates.io/crates/#{CGI.escape(name)}/#{CGI.escape(version)}"
26
+ end
23
27
  end
24
28
  end
@@ -14,5 +14,9 @@ module LicenseFinder
14
14
  def package_manager
15
15
  'CocoaPods'
16
16
  end
17
+
18
+ def package_url
19
+ "https://cocoapods.org/pods/#{CGI.escape(name)}"
20
+ end
17
21
  end
18
22
  end
@@ -5,5 +5,9 @@ module LicenseFinder
5
5
  def package_manager
6
6
  'Composer'
7
7
  end
8
+
9
+ def package_url
10
+ "https://packagist.org/packages/#{CGI.escape(name)}##{CGI.escape(version)}"
11
+ end
8
12
  end
9
13
  end