license_finder 6.3.0 → 6.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/CHANGELOG.md +60 -0
  4. data/Dockerfile +13 -11
  5. data/README.md +28 -6
  6. data/Rakefile +1 -1
  7. data/VERSION +1 -1
  8. data/ci/pipelines/release.yml.erb +14 -4
  9. data/ci/tasks/rubocop.yml +1 -1
  10. data/lib/license_finder/cli.rb +1 -0
  11. data/lib/license_finder/cli/base.rb +1 -0
  12. data/lib/license_finder/cli/inherited_decisions.rb +50 -0
  13. data/lib/license_finder/cli/main.rb +3 -1
  14. data/lib/license_finder/configuration.rb +5 -1
  15. data/lib/license_finder/decision_applier.rb +8 -4
  16. data/lib/license_finder/decisions.rb +99 -20
  17. data/lib/license_finder/license.rb +37 -0
  18. data/lib/license_finder/license/definitions.rb +26 -3
  19. data/lib/license_finder/license/templates/0BSD.txt +10 -0
  20. data/lib/license_finder/license/templates/SimplifiedBSD.txt +0 -4
  21. data/lib/license_finder/license/text.rb +24 -2
  22. data/lib/license_finder/logger.rb +2 -0
  23. data/lib/license_finder/package.rb +2 -1
  24. data/lib/license_finder/package_manager.rb +6 -2
  25. data/lib/license_finder/package_managers/bundler.rb +1 -1
  26. data/lib/license_finder/package_managers/dotnet.rb +2 -1
  27. data/lib/license_finder/package_managers/go_15vendorexperiment.rb +1 -1
  28. data/lib/license_finder/package_managers/go_modules.rb +35 -12
  29. data/lib/license_finder/package_managers/mix.rb +1 -1
  30. data/lib/license_finder/package_managers/nuget.rb +51 -4
  31. data/lib/license_finder/package_managers/pipenv.rb +1 -1
  32. data/lib/license_finder/package_managers/rebar.rb +29 -8
  33. data/lib/license_finder/package_managers/yarn.rb +16 -2
  34. data/lib/license_finder/package_utils/license_files.rb +2 -2
  35. data/lib/license_finder/packages/bower_package.rb +7 -0
  36. data/lib/license_finder/packages/bundler_package.rb +4 -0
  37. data/lib/license_finder/packages/cargo_package.rb +4 -0
  38. data/lib/license_finder/packages/cocoa_pods_package.rb +4 -0
  39. data/lib/license_finder/packages/composer_package.rb +4 -0
  40. data/lib/license_finder/packages/conan_package.rb +4 -0
  41. data/lib/license_finder/packages/go_package.rb +4 -0
  42. data/lib/license_finder/packages/gradle_package.rb +4 -0
  43. data/lib/license_finder/packages/maven_package.rb +4 -0
  44. data/lib/license_finder/packages/merged_package.rb +1 -1
  45. data/lib/license_finder/packages/mix_package.rb +4 -0
  46. data/lib/license_finder/packages/npm_package.rb +4 -0
  47. data/lib/license_finder/packages/nuget_package.rb +4 -0
  48. data/lib/license_finder/packages/pip_package.rb +13 -2
  49. data/lib/license_finder/packages/rebar_package.rb +4 -0
  50. data/lib/license_finder/packages/yarn_package.rb +4 -0
  51. data/lib/license_finder/reports/csv_report.rb +7 -3
  52. data/lib/license_finder/reports/json_report.rb +2 -0
  53. metadata +5 -3
@@ -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
 
@@ -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
@@ -4,7 +4,7 @@ require 'license_finder/packages/go_package'
4
4
 
5
5
  module LicenseFinder
6
6
  class GoModules < PackageManager
7
- PACKAGES_FILE = 'go.sum'
7
+ PACKAGES_FILE = 'go.mod'
8
8
 
9
9
  class << self
10
10
  def takes_priority_over
@@ -12,12 +12,8 @@ module LicenseFinder
12
12
  end
13
13
  end
14
14
 
15
- def prepare_command
16
- 'GO111MODULE=on go mod tidy && GO111MODULE=on go mod vendor'
17
- end
18
-
19
15
  def active?
20
- sum_files?
16
+ mod_files?
21
17
  end
22
18
 
23
19
  def current_packages
@@ -33,17 +29,44 @@ module LicenseFinder
33
29
  private
34
30
 
35
31
  def packages_info
36
- info_output, stderr, _status = Cmd.run("GO111MODULE=on go list -m -mod=vendor -f '{{.Path}},{{.Version}},{{.Dir}}' all")
37
- info_output, _stderr, _status = Cmd.run("GO111MODULE=on go list -m -f '{{.Path}},{{.Version}},{{.Dir}}' all") if stderr =~ Regexp.compile("can't compute 'all' using the vendor directory")
32
+ Dir.chdir(project_path) do
33
+ # Explanations:
34
+ # * Only list dependencies (packages not listed in the project directory)
35
+ # (.DepOnly)
36
+ # * Ignore standard library packages
37
+ # (not .Standard)
38
+ # * Replacement modules are respected
39
+ # (or .Module.Replace .Module)
40
+ # * Module cache directory or (vendored) package directory
41
+ # (or $mod.Dir .Dir)
42
+ format_str = \
43
+ '{{ if and (.DepOnly) (not .Standard) }}'\
44
+ '{{ $mod := (or .Module.Replace .Module) }}'\
45
+ '{{ $mod.Path }},{{ $mod.Version }},{{ or $mod.Dir .Dir }}'\
46
+ '{{ end }}'
38
47
 
39
- info_output.split("\n")
48
+ # The module list flag (`-m`) is intentionally not used here. If the module
49
+ # dependency tree were followed, transitive dependencies that are never imported
50
+ # may be included.
51
+ #
52
+ # Instead, the owning module is listed for each imported package. This better
53
+ # matches the implementation of other Go package managers.
54
+ #
55
+ # TODO: Figure out a way to make the vendor directory work (i.e. remove the
56
+ # -mod=readonly flag). Each of the imported packages gets listed separatly,
57
+ # confusing the issue as to which package is the root of the module.
58
+ info_output, _stderr, _status = Cmd.run("GO111MODULE=on go list -mod=readonly -deps -f '#{format_str}' ./...")
59
+
60
+ # Since many packages may belong to a single module, #uniq is used to deduplicate
61
+ info_output.split("\n").uniq
62
+ end
40
63
  end
41
64
 
42
- def sum_files?
43
- sum_file_paths.any?
65
+ def mod_files?
66
+ mod_file_paths.any?
44
67
  end
45
68
 
46
- def sum_file_paths
69
+ def mod_file_paths
47
70
  Dir[project_path.join(PACKAGES_FILE)]
48
71
  end
49
72
 
@@ -96,7 +96,7 @@ module LicenseFinder
96
96
  raise "Command '#{command}' failed to execute: #{stderr}" unless status.success?
97
97
 
98
98
  packages_lines(stdout)
99
- .reject { |package_lines| package_lines.length == 1 } # in_umbrella: true dependencies
99
+ .reject { |package_lines| package_lines.length == 1 || package_lines.empty? } # in_umbrella: true dependencies
100
100
  .map { |package_lines| [package_lines[0].split(' ')[1], resolve_version(package_lines[1])] }
101
101
  end
102
102
 
@@ -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,14 +73,59 @@ 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
+ Dir.chdir(project_path) do
94
+ cmd = prepare_command
95
+ stdout, stderr, status = Cmd.run(cmd)
96
+ return if status.success?
97
+
98
+ log_errors stderr
99
+
100
+ if stderr.include?('-PackagesDirectory')
101
+ logger.info cmd, 'trying fallback prepare command', color: :magenta
102
+
103
+ cmd = "#{cmd} -PackagesDirectory /#{Dir.home}/.nuget/packages"
104
+ stdout, stderr, status = Cmd.run(cmd)
105
+ return if status.success?
106
+
107
+ log_errors_with_cmd(cmd, stderr)
108
+ end
109
+
110
+ error_message = "Prepare command '#{cmd}' failed\n#{stderr}"
111
+ error_message += "\n#{stdout}\n" if !stdout.nil? && !stdout.empty?
112
+ raise error_message unless @prepare_no_fail
113
+ end
78
114
  end
79
115
 
80
116
  def prepare_command
81
- "#{package_management_command} restore"
117
+ cmd = package_management_command
118
+ sln_files = Dir['*.sln']
119
+ cmds = []
120
+ if sln_files.count > 1
121
+ sln_files.each do |sln|
122
+ cmds << "#{cmd} restore #{sln}"
123
+ end
124
+ else
125
+ cmds << "#{cmd} restore"
126
+ end
127
+
128
+ cmds.join(' && ')
82
129
  end
83
130
 
84
131
  def installed?(logger = Core.default_logger)
@@ -94,7 +141,7 @@ module LicenseFinder
94
141
  def nuget_check
95
142
  return 'where nuget' if LicenseFinder::Platform.windows?
96
143
 
97
- 'which mono && ls /usr/local/bin/nuget.exe'
144
+ "which mono && ls #{nuget_binary}"
98
145
  end
99
146
 
100
147
  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
@@ -5,23 +5,25 @@ module LicenseFinder
5
5
  def initialize(options = {})
6
6
  super
7
7
  @command = options[:rebar_command] || package_management_command
8
- @deps_path = Pathname(options[:rebar_deps_dir] || 'deps')
8
+ @deps_path = Pathname(options[:rebar_deps_dir] || File.join(project_path, '_build/default/lib'))
9
9
  end
10
10
 
11
11
  def current_packages
12
- rebar_ouput.map do |name, version_type, version_value, homepage|
12
+ rebar_deps.map do |name, version|
13
+ licenses, homepage = dep_info(name)
13
14
  RebarPackage.new(
14
15
  name,
15
- "#{version_type}: #{version_value}",
16
+ version,
16
17
  install_path: @deps_path.join(name),
17
18
  homepage: homepage,
19
+ spec_licenses: licenses.nil? ? [] : [licenses],
18
20
  logger: logger
19
21
  )
20
22
  end
21
23
  end
22
24
 
23
25
  def package_management_command
24
- 'rebar'
26
+ 'rebar3'
25
27
  end
26
28
 
27
29
  def possible_package_paths
@@ -30,15 +32,34 @@ module LicenseFinder
30
32
 
31
33
  private
32
34
 
33
- def rebar_ouput
34
- command = "#{@command} list-deps"
35
+ def rebar_deps
36
+ command = "#{@command} tree"
35
37
  stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(command) }
36
38
  raise "Command '#{command}' failed to execute: #{stderr}" unless status.success?
37
39
 
38
40
  stdout
39
41
  .each_line
40
- .reject { |line| line.start_with?('=') }
41
- .map { |line| line.split(' ') }
42
+ .reject { |line| line.start_with?('=') || line.include?('project app') }
43
+ .map do |line|
44
+ matches = line.match(/(?<name>\w+)─(?<version>[\S.]+)\s*/)
45
+ [matches[:name], matches[:version]] if matches
46
+ end.compact
47
+ end
48
+
49
+ def dep_info(name)
50
+ command = "#{@command} pkgs #{name}"
51
+ stdout, _, status = Cmd.run(command)
52
+ return [nil, nil] unless status.success?
53
+
54
+ licenses = nil
55
+ homepage = nil
56
+
57
+ stdout.scan(/Licenses: (?<licenses>.+)|(?<homepage>(https|http).*)/) do |pkg_licenses, pkg_homepage|
58
+ licenses ||= pkg_licenses
59
+ homepage ||= pkg_homepage
60
+ end
61
+
62
+ [licenses, homepage]
42
63
  end
43
64
  end
44
65
  end
@@ -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
@@ -15,5 +15,9 @@ module LicenseFinder
15
15
  def package_manager
16
16
  'Conan'
17
17
  end
18
+
19
+ def package_url
20
+ "https://conan.io/center/#{CGI.escape(name)}/#{CGI.escape(version)}"
21
+ end
18
22
  end
19
23
  end
@@ -8,6 +8,10 @@ module LicenseFinder
8
8
  'Go'
9
9
  end
10
10
 
11
+ def package_url
12
+ "https://pkg.go.dev/#{CGI.escape(name)}@#{CGI.escape(version)}"
13
+ end
14
+
11
15
  class << self
12
16
  def from_dependency(hash, prefix, full_version)
13
17
  name = hash['ImportPath']
@@ -22,5 +22,9 @@ module LicenseFinder
22
22
  def package_manager
23
23
  'Gradle'
24
24
  end
25
+
26
+ def package_url
27
+ "https://plugins.gradle.org/plugin/#{CGI.escape(name)}/#{CGI.escape(version)}"
28
+ end
25
29
  end
26
30
  end
@@ -19,5 +19,9 @@ module LicenseFinder
19
19
  def package_manager
20
20
  'Maven'
21
21
  end
22
+
23
+ def package_url
24
+ "https://search.maven.org/artifact/#{CGI.escape(groups.first)}/#{CGI.escape(name.split(':').last)}/#{CGI.escape(version)}/jar"
25
+ end
22
26
  end
23
27
  end
@@ -11,7 +11,7 @@ module LicenseFinder
11
11
  super(package.name, package.version)
12
12
  end
13
13
 
14
- def_delegators :@dependency, :name, :version, :authors, :summary, :description, :homepage, :children, :parents,
14
+ def_delegators :@dependency, :name, :version, :authors, :summary, :description, :homepage, :package_url, :children, :parents,
15
15
  :groups, :permitted, :restricted, :manual_approval, :install_path, :licenses, :approved_manually?,
16
16
  :approved_manually!, :approved?, :permitted!, :permitted?, :restricted!, :restricted?, :hash,
17
17
  :activations, :missing, :license_names_from_spec, :decided_licenses, :licensing, :decide_on_license,
@@ -5,5 +5,9 @@ module LicenseFinder
5
5
  def package_manager
6
6
  'Mix'
7
7
  end
8
+
9
+ def package_url
10
+ "https://hex.pm/packages/#{CGI.escape(name)}/#{CGI.escape(version)}"
11
+ end
8
12
  end
9
13
  end
@@ -89,6 +89,10 @@ module LicenseFinder
89
89
  'Npm'
90
90
  end
91
91
 
92
+ def package_url
93
+ "https://www.npmjs.com/package/#{CGI.escape(name)}/v/#{CGI.escape(version)}"
94
+ end
95
+
92
96
  private
93
97
 
94
98
  def deps_from_json