license_finder 6.5.0 → 6.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -0
- data/CONTRIBUTING.md +5 -4
- data/Dockerfile +26 -9
- data/README.md +53 -15
- data/Rakefile +1 -10
- data/VERSION +1 -1
- data/ci/pipelines/pull-request.yml.erb +2 -0
- data/ci/pipelines/release.yml.erb +16 -4
- data/ci/tasks/rubocop.yml +2 -0
- data/ci/tasks/update-changelog.yml +2 -0
- data/examples/Gemfile +4 -0
- data/examples/custom_erb_template.rb +24 -0
- data/examples/extract_license_data.rb +63 -0
- data/examples/sample_template.erb +7 -0
- data/lib/license_finder/cli/base.rb +8 -1
- data/lib/license_finder/cli/inherited_decisions.rb +18 -0
- data/lib/license_finder/cli/main.rb +5 -1
- data/lib/license_finder/configuration.rb +13 -1
- data/lib/license_finder/core.rb +5 -2
- data/lib/license_finder/decisions.rb +58 -10
- data/lib/license_finder/license.rb +45 -1
- data/lib/license_finder/license/definitions.rb +49 -2
- data/lib/license_finder/license/header_matcher.rb +7 -2
- data/lib/license_finder/license/templates/0BSD.txt +10 -0
- data/lib/license_finder/license/templates/MPL1_1.txt +469 -0
- data/lib/license_finder/license/text.rb +2 -2
- data/lib/license_finder/logger.rb +2 -0
- data/lib/license_finder/package.rb +2 -0
- data/lib/license_finder/package_manager.rb +15 -5
- data/lib/license_finder/package_managers/composer.rb +8 -4
- data/lib/license_finder/package_managers/conda.rb +131 -0
- data/lib/license_finder/package_managers/dep.rb +6 -1
- data/lib/license_finder/package_managers/dotnet.rb +2 -1
- data/lib/license_finder/package_managers/erlangmk.rb +50 -0
- data/lib/license_finder/package_managers/go_15vendorexperiment.rb +6 -1
- data/lib/license_finder/package_managers/go_dep.rb +15 -8
- data/lib/license_finder/package_managers/go_modules.rb +43 -15
- data/lib/license_finder/package_managers/mix.rb +1 -1
- data/lib/license_finder/package_managers/npm.rb +1 -1
- data/lib/license_finder/package_managers/nuget.rb +36 -1
- data/lib/license_finder/package_managers/pipenv.rb +1 -1
- data/lib/license_finder/package_managers/rebar.rb +29 -8
- data/lib/license_finder/package_managers/trash.rb +6 -1
- data/lib/license_finder/package_managers/yarn.rb +1 -1
- data/lib/license_finder/packages/conda_package.rb +74 -0
- data/lib/license_finder/packages/erlangmk_package.rb +114 -0
- data/lib/license_finder/packages/pip_package.rb +9 -2
- data/lib/license_finder/report.rb +1 -0
- data/lib/license_finder/reports/junit_report.rb +19 -0
- data/lib/license_finder/reports/templates/junit_report.erb +41 -0
- data/lib/license_finder/scanner.rb +25 -2
- data/license_finder.gemspec +3 -2
- metadata +41 -9
data/ci/tasks/rubocop.yml
CHANGED
data/examples/Gemfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'bundler/setup'
|
7
|
+
|
8
|
+
# This is an example of how to programatically generate a report using a custom
|
9
|
+
# ERB template. Run with
|
10
|
+
# > bundle install
|
11
|
+
# > ./custom_erb_template.rb
|
12
|
+
|
13
|
+
require 'license_finder'
|
14
|
+
|
15
|
+
# See lib/license_finder/core.rb for more configuration options.
|
16
|
+
# A quiet logger is required when running reports...
|
17
|
+
lf = LicenseFinder::Core.new(LicenseFinder::Configuration.with_optional_saved_config(logger: :quiet))
|
18
|
+
|
19
|
+
# Find many more examples of complex ERB templates in
|
20
|
+
# lib/license_finder/reports/templates/
|
21
|
+
template = Pathname.new('./sample_template.erb')
|
22
|
+
print LicenseFinder::ErbReport
|
23
|
+
.new(lf.acknowledged, project_name: lf.project_name)
|
24
|
+
.to_s(template)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'bundler/setup'
|
7
|
+
|
8
|
+
# This is an example of how to programatically extract the information that
|
9
|
+
# LicenseFinder has about packages and their licenses.
|
10
|
+
# > bundle install
|
11
|
+
# > ./extract_license_data.rb
|
12
|
+
|
13
|
+
require 'license_finder'
|
14
|
+
|
15
|
+
# See lib/license_finder/core.rb for more configuration options.
|
16
|
+
# A quiet logger is required when running reports...
|
17
|
+
lf = LicenseFinder::Core.new(LicenseFinder::Configuration.with_optional_saved_config(logger: :quiet))
|
18
|
+
|
19
|
+
# Groups of packages
|
20
|
+
lf.acknowledged # All (non-ignored) packages license_finder is tracking
|
21
|
+
lf.unapproved # The packages which have not been approved or permitted
|
22
|
+
lf.restricted # The packages which have been restricted
|
23
|
+
|
24
|
+
# Package details
|
25
|
+
lf.acknowledged.each do |package|
|
26
|
+
# Approvals
|
27
|
+
package.approved? # Whether the package has been approved manually or permitted
|
28
|
+
package.approved_manually?
|
29
|
+
package.permitted?
|
30
|
+
package.restricted?
|
31
|
+
|
32
|
+
# Licensing
|
33
|
+
# The set of licenses, each of which has a name and url, which
|
34
|
+
# license_finder will report for this package.
|
35
|
+
package.licenses
|
36
|
+
# Additional information about how these licenses were chosen
|
37
|
+
# (from decision, from spec, from files, or none-found). See
|
38
|
+
# LicenseFinder::Licensing and LicenseFinder::Activation
|
39
|
+
package.activations
|
40
|
+
# The files that look like licenses, found in the package's
|
41
|
+
# directory. Caveat: if a package's licenses were specified by a decision or
|
42
|
+
# by the package's spec, the license_files will be ignored. That means,
|
43
|
+
# package.licenses may report different licenses than those found in
|
44
|
+
# license_files.
|
45
|
+
package.license_files
|
46
|
+
package.license_files.each do |file|
|
47
|
+
# The license found in this file.
|
48
|
+
file.license
|
49
|
+
# The text of the file. Sometimes this will be an entire README file,
|
50
|
+
# because license_finder has found the phrase "is released under the
|
51
|
+
# MIT license" in it.
|
52
|
+
file.text
|
53
|
+
end
|
54
|
+
package.licensing.activations_from_decisions # If license_finder only knew about decisions, what licenses would it report?
|
55
|
+
package.licensing.activations_from_spec # If license_finder only knew about package specs, what licenses would it report?
|
56
|
+
package.licensing.activations_from_files # If license_finder only knew about package files, what licenses would it report?
|
57
|
+
package.licensing.activations_from_files.each do |activation|
|
58
|
+
# Each activation groups together all files that point to the same license.
|
59
|
+
# Each file contains its #license and #text.
|
60
|
+
activation.license
|
61
|
+
activation.files
|
62
|
+
end
|
63
|
+
end
|
@@ -11,6 +11,10 @@ module LicenseFinder
|
|
11
11
|
desc: 'Where decisions are saved. Defaults to doc/dependency_decisions.yml.'
|
12
12
|
class_option :log_directory,
|
13
13
|
desc: 'Where logs are saved. Defaults to ./lf_logs/$PROJECT/prepare_$PACKAGE_MANAGER.log'
|
14
|
+
class_option :enabled_package_managers,
|
15
|
+
desc: 'List of package managers to be enabled. Defaults to all supported package managers.',
|
16
|
+
type: :array,
|
17
|
+
enum: LicenseFinder::Scanner.supported_package_manager_ids
|
14
18
|
|
15
19
|
no_commands do
|
16
20
|
def decisions
|
@@ -32,6 +36,7 @@ module LicenseFinder
|
|
32
36
|
extract_options(
|
33
37
|
:project_path,
|
34
38
|
:decisions_file,
|
39
|
+
:enabled_package_managers,
|
35
40
|
:go_full_version,
|
36
41
|
:gradle_command,
|
37
42
|
:gradle_include_groups,
|
@@ -53,7 +58,9 @@ module LicenseFinder
|
|
53
58
|
:columns,
|
54
59
|
:aggregate_paths,
|
55
60
|
:recursive,
|
56
|
-
:sbt_include_groups
|
61
|
+
:sbt_include_groups,
|
62
|
+
:conda_bash_setup_script,
|
63
|
+
:composer_check_require_only
|
57
64
|
).merge(
|
58
65
|
logger: logger_mode
|
59
66
|
)
|
@@ -20,6 +20,15 @@ module LicenseFinder
|
|
20
20
|
say "Added #{decision_files.join(', ')} to the inherited decisions"
|
21
21
|
end
|
22
22
|
|
23
|
+
auditable
|
24
|
+
desc 'add_with_auth URL AUTH_TYPE TOKEN_OR_ENV', 'Add a remote decision file that needs authentication'
|
25
|
+
def add_with_auth(*params)
|
26
|
+
url, auth_type, token_or_env = params
|
27
|
+
auth_info = { 'url' => url, 'authorization' => "#{auth_type} #{token_or_env}" }
|
28
|
+
modifying { decisions.add_decision [:inherit_from, auth_info] }
|
29
|
+
say "Added #{url} to the inherited decisions"
|
30
|
+
end
|
31
|
+
|
23
32
|
auditable
|
24
33
|
desc 'remove DECISION_FILE...', 'Remove one or more decision files from the inherited decisions'
|
25
34
|
def remove(*decision_files)
|
@@ -27,6 +36,15 @@ module LicenseFinder
|
|
27
36
|
modifying { decision_files.each { |filepath| decisions.remove_inheritance(filepath) } }
|
28
37
|
say "Removed #{decision_files.join(', ')} from the inherited decisions"
|
29
38
|
end
|
39
|
+
|
40
|
+
auditable
|
41
|
+
desc 'remove_with_auth URL AUTH_TYPE TOKEN_OR_ENV', 'Add a remote decision file that needs authentication'
|
42
|
+
def remove_with_auth(*params)
|
43
|
+
url, auth_type, token_or_env = params
|
44
|
+
auth_info = { 'url' => url, 'authorization' => "#{auth_type} #{token_or_env}" }
|
45
|
+
modifying { decisions.remove_inheritance(auth_info) }
|
46
|
+
say "Removed #{url} from the inherited decisions"
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
32
50
|
end
|
@@ -19,7 +19,8 @@ module LicenseFinder
|
|
19
19
|
'markdown' => MarkdownReport,
|
20
20
|
'csv' => CsvReport,
|
21
21
|
'xml' => XmlReport,
|
22
|
-
'json' => JsonReport
|
22
|
+
'json' => JsonReport,
|
23
|
+
'junit' => JunitReport
|
23
24
|
}.freeze
|
24
25
|
|
25
26
|
class_option :go_full_version, desc: 'Whether dependency version should include full version. Only meaningful if used with a Go project. Defaults to false.'
|
@@ -37,6 +38,9 @@ module LicenseFinder
|
|
37
38
|
class_option :mix_command, desc: "Command to use when fetching packages through Mix. Only meaningful if used with a Mix project (i.e., Elixir or Erlang). Defaults to 'mix'."
|
38
39
|
class_option :mix_deps_dir, desc: "Path to Mix dependencies directory. Only meaningful if used with a Mix project (i.e., Elixir or Erlang). Defaults to 'deps'."
|
39
40
|
class_option :sbt_include_groups, desc: 'Whether dependency name should include group id. Only meaningful if used with a Scala/sbt project. Defaults to false.'
|
41
|
+
class_option :conda_bash_setup_script, desc: "Path to conda.sh script. Only meaningful if used with a Conda project. Defaults to '~/miniconda3/etc/profile.d/conda.sh'."
|
42
|
+
class_option :composer_check_require_only,
|
43
|
+
desc: "Whether to only check for licenses from dependencies on the 'require' section. Only meaningful if used with a Composer project. Defaults to false."
|
40
44
|
|
41
45
|
# Method options which are shared between report and action_item
|
42
46
|
def self.format_option
|
@@ -35,7 +35,7 @@ module LicenseFinder
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def rebar_deps_dir
|
38
|
-
path = get(:rebar_deps_dir) || '
|
38
|
+
path = get(:rebar_deps_dir) || '_build/default/lib'
|
39
39
|
project_path.join(path).expand_path
|
40
40
|
end
|
41
41
|
|
@@ -65,6 +65,10 @@ module LicenseFinder
|
|
65
65
|
Pathname(path_prefix).expand_path
|
66
66
|
end
|
67
67
|
|
68
|
+
def enabled_package_manager_ids
|
69
|
+
get(:enabled_package_managers)
|
70
|
+
end
|
71
|
+
|
68
72
|
def logger_mode
|
69
73
|
get(:logger)
|
70
74
|
end
|
@@ -93,6 +97,10 @@ module LicenseFinder
|
|
93
97
|
get(:pip_requirements_path)
|
94
98
|
end
|
95
99
|
|
100
|
+
def conda_bash_setup_script
|
101
|
+
get(:conda_bash_setup_script)
|
102
|
+
end
|
103
|
+
|
96
104
|
def python_version
|
97
105
|
get(:python_version)
|
98
106
|
end
|
@@ -137,6 +145,10 @@ module LicenseFinder
|
|
137
145
|
get(:sbt_include_groups)
|
138
146
|
end
|
139
147
|
|
148
|
+
def composer_check_require_only
|
149
|
+
get(:composer_check_require_only)
|
150
|
+
end
|
151
|
+
|
140
152
|
attr_writer :strict_matching
|
141
153
|
|
142
154
|
attr_reader :strict_matching
|
data/lib/license_finder/core.rb
CHANGED
@@ -24,7 +24,7 @@ module LicenseFinder
|
|
24
24
|
# Default +options+:
|
25
25
|
# {
|
26
26
|
# project_path: Pathname.pwd
|
27
|
-
# logger:
|
27
|
+
# logger: nil, # can be :quiet or :debug
|
28
28
|
# decisions_file: "doc/dependency_decisions.yml",
|
29
29
|
# gradle_command: "gradle",
|
30
30
|
# rebar_command: "rebar",
|
@@ -93,6 +93,7 @@ module LicenseFinder
|
|
93
93
|
project_path: config.project_path,
|
94
94
|
log_directory: File.join(config.log_directory, project_name),
|
95
95
|
ignored_groups: decisions.ignored_groups,
|
96
|
+
enabled_package_manager_ids: config.enabled_package_manager_ids,
|
96
97
|
go_full_version: config.go_full_version,
|
97
98
|
gradle_command: config.gradle_command,
|
98
99
|
gradle_include_groups: config.gradle_include_groups,
|
@@ -107,7 +108,9 @@ module LicenseFinder
|
|
107
108
|
mix_deps_dir: config.mix_deps_dir,
|
108
109
|
prepare: config.prepare,
|
109
110
|
prepare_no_fail: config.prepare_no_fail,
|
110
|
-
sbt_include_groups: config.sbt_include_groups
|
111
|
+
sbt_include_groups: config.sbt_include_groups,
|
112
|
+
conda_bash_setup_script: config.conda_bash_setup_script,
|
113
|
+
composer_check_require_only: config.composer_check_require_only
|
111
114
|
}
|
112
115
|
end
|
113
116
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'open-uri'
|
4
|
+
require 'license_finder/license'
|
4
5
|
|
5
6
|
module LicenseFinder
|
6
7
|
class Decisions
|
@@ -39,7 +40,15 @@ module LicenseFinder
|
|
39
40
|
end
|
40
41
|
|
41
42
|
def permitted?(lic)
|
42
|
-
@permitted.include?(lic)
|
43
|
+
if @permitted.include?(lic)
|
44
|
+
true
|
45
|
+
elsif lic.is_a?(OrLicense)
|
46
|
+
lic.sub_licenses.any? { |sub_lic| @permitted.include?(sub_lic) }
|
47
|
+
elsif lic.is_a?(AndLicense)
|
48
|
+
lic.sub_licenses.all? { |sub_lic| @permitted.include?(sub_lic) }
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
43
52
|
end
|
44
53
|
|
45
54
|
def restricted?(lic)
|
@@ -183,19 +192,37 @@ module LicenseFinder
|
|
183
192
|
self
|
184
193
|
end
|
185
194
|
|
186
|
-
def inherit_from(
|
195
|
+
def inherit_from(filepath_info)
|
187
196
|
decisions =
|
188
|
-
if
|
189
|
-
|
197
|
+
if filepath_info.is_a?(Hash)
|
198
|
+
resolve_inheritance(filepath_info)
|
199
|
+
elsif filepath_info =~ %r{^https?://}
|
200
|
+
open_uri(filepath_info).read
|
190
201
|
else
|
191
|
-
Pathname(
|
202
|
+
Pathname(filepath_info).read
|
192
203
|
end
|
193
204
|
|
194
|
-
add_decision [:inherit_from,
|
195
|
-
@inherited_decisions <<
|
205
|
+
add_decision [:inherit_from, filepath_info]
|
206
|
+
@inherited_decisions << filepath_info
|
196
207
|
restore_inheritance(decisions)
|
197
208
|
end
|
198
209
|
|
210
|
+
def resolve_inheritance(filepath_info)
|
211
|
+
if (gem_name = filepath_info['gem'])
|
212
|
+
Pathname(gem_config_path(gem_name, filepath_info['path'])).read
|
213
|
+
else
|
214
|
+
open_uri(filepath_info['url'], filepath_info['authorization']).read
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def gem_config_path(gem_name, relative_config_path)
|
219
|
+
spec = Gem::Specification.find_by_name(gem_name)
|
220
|
+
File.join(spec.gem_dir, relative_config_path)
|
221
|
+
rescue Gem::LoadError => e
|
222
|
+
raise Gem::LoadError,
|
223
|
+
"Unable to find gem #{gem_name}; is the gem installed? #{e}"
|
224
|
+
end
|
225
|
+
|
199
226
|
def remove_inheritance(filepath)
|
200
227
|
@decisions -= [[:inherit_from, filepath]]
|
201
228
|
@inherited_decisions.delete(filepath)
|
@@ -213,17 +240,31 @@ module LicenseFinder
|
|
213
240
|
self
|
214
241
|
end
|
215
242
|
|
216
|
-
def open_uri(uri)
|
243
|
+
def open_uri(uri, auth = nil)
|
244
|
+
header = {}
|
245
|
+
auth_header = resolve_authorization(auth)
|
246
|
+
header['Authorization'] = auth_header if auth_header
|
247
|
+
|
217
248
|
# ruby < 2.5.0 URI.open is private
|
218
249
|
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0')
|
219
250
|
# rubocop:disable Security/Open
|
220
|
-
open(uri)
|
251
|
+
open(uri, header)
|
221
252
|
# rubocop:enable Security/Open
|
222
253
|
else
|
223
|
-
URI.open(uri)
|
254
|
+
URI.open(uri, header)
|
224
255
|
end
|
225
256
|
end
|
226
257
|
|
258
|
+
def resolve_authorization(auth)
|
259
|
+
return unless auth
|
260
|
+
|
261
|
+
token_env = auth.match(/\$(\S.*)/)
|
262
|
+
return auth unless token_env
|
263
|
+
|
264
|
+
token = ENV[token_env[1]]
|
265
|
+
auth.sub(token_env[0], token)
|
266
|
+
end
|
267
|
+
|
227
268
|
#########
|
228
269
|
# PERSIST
|
229
270
|
#########
|
@@ -240,6 +281,13 @@ module LicenseFinder
|
|
240
281
|
return result unless persisted
|
241
282
|
|
242
283
|
actions = YAML.load(persisted)
|
284
|
+
|
285
|
+
list_of_actions = (actions || []).map(&:first)
|
286
|
+
|
287
|
+
if (list_of_actions & %i[whitelist blacklist]).any?
|
288
|
+
raise 'The decisions file seems to have whitelist/blacklist keys which are deprecated. Please replace them with permit/restrict respectively and try again! More info - https://github.com/pivotal/LicenseFinder/commit/a40b22fda11b3a0efbb3c0a021381534bc998dd9'
|
289
|
+
end
|
290
|
+
|
243
291
|
(actions || []).each do |action, *args|
|
244
292
|
result.send(action, *args)
|
245
293
|
end
|
@@ -19,7 +19,17 @@ module LicenseFinder
|
|
19
19
|
|
20
20
|
def find_by_name(name)
|
21
21
|
name ||= 'unknown'
|
22
|
-
all.detect { |l| l.matches_name? l.stripped_name(name) }
|
22
|
+
license = all.detect { |l| l.matches_name? l.stripped_name(name) }
|
23
|
+
|
24
|
+
if license
|
25
|
+
license
|
26
|
+
elsif name.include?(OrLicense.operator)
|
27
|
+
OrLicense.new(name)
|
28
|
+
elsif name.include?(AndLicense.operator)
|
29
|
+
AndLicense.new(name)
|
30
|
+
else
|
31
|
+
Definitions.build_unrecognized(name)
|
32
|
+
end
|
23
33
|
end
|
24
34
|
|
25
35
|
def find_by_text(text)
|
@@ -61,6 +71,10 @@ module LicenseFinder
|
|
61
71
|
name.hash
|
62
72
|
end
|
63
73
|
|
74
|
+
def unrecognized_matcher?
|
75
|
+
matcher.is_a?(NoneMatcher)
|
76
|
+
end
|
77
|
+
|
64
78
|
private
|
65
79
|
|
66
80
|
attr_reader :short_name, :pretty_name, :other_names
|
@@ -70,4 +84,34 @@ module LicenseFinder
|
|
70
84
|
([short_name, pretty_name] + other_names).uniq
|
71
85
|
end
|
72
86
|
end
|
87
|
+
class AndLicense < License
|
88
|
+
def self.operator
|
89
|
+
' AND '
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(name, operator = AndLicense.operator)
|
93
|
+
@short_name = name
|
94
|
+
@pretty_name = name
|
95
|
+
@url = nil
|
96
|
+
@matcher = NoneMatcher.new
|
97
|
+
# removes heading and trailing parentesis and splits
|
98
|
+
name = name[1..-2] if name.start_with?('(')
|
99
|
+
names = name.split(operator)
|
100
|
+
@sub_licenses = names.map do |sub_name|
|
101
|
+
License.find_by_name(sub_name)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
attr_reader :sub_licenses
|
106
|
+
end
|
107
|
+
|
108
|
+
class OrLicense < AndLicense
|
109
|
+
def self.operator
|
110
|
+
' OR '
|
111
|
+
end
|
112
|
+
|
113
|
+
def initialize(name)
|
114
|
+
super(name, OrLicense.operator)
|
115
|
+
end
|
116
|
+
end
|
73
117
|
end
|