spandx 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -2
  3. data/exe/spandx +0 -1
  4. data/ext/spandx/spandx.c +6 -4
  5. data/lib/spandx.rb +1 -1
  6. data/lib/spandx/cli.rb +2 -1
  7. data/lib/spandx/cli/commands/scan.rb +15 -32
  8. data/lib/spandx/cli/main.rb +3 -3
  9. data/lib/spandx/cli/printer.rb +27 -0
  10. data/lib/spandx/cli/printers/csv.rb +17 -0
  11. data/lib/spandx/cli/printers/json.rb +17 -0
  12. data/lib/spandx/cli/printers/table.rb +41 -0
  13. data/lib/spandx/core/dependency.rb +48 -13
  14. data/lib/spandx/core/git.rb +6 -8
  15. data/lib/spandx/core/guess.rb +12 -1
  16. data/lib/spandx/core/http.rb +7 -7
  17. data/lib/spandx/core/index_file.rb +2 -0
  18. data/lib/spandx/core/license_plugin.rb +15 -4
  19. data/lib/spandx/core/parser.rb +10 -3
  20. data/lib/spandx/core/path_traversal.rb +4 -13
  21. data/lib/spandx/core/plugin.rb +6 -0
  22. data/lib/spandx/core/thread_pool.rb +11 -11
  23. data/lib/spandx/dotnet/nuget_gateway.rb +1 -1
  24. data/lib/spandx/dotnet/parsers/csproj.rb +7 -7
  25. data/lib/spandx/dotnet/parsers/packages_config.rb +7 -7
  26. data/lib/spandx/dotnet/parsers/sln.rb +10 -13
  27. data/lib/spandx/dotnet/project_file.rb +3 -3
  28. data/lib/spandx/java/parsers/maven.rb +7 -7
  29. data/lib/spandx/js/parsers/npm.rb +8 -8
  30. data/lib/spandx/js/parsers/yarn.rb +7 -7
  31. data/lib/spandx/js/yarn_pkg.rb +1 -1
  32. data/lib/spandx/os/parsers/apk.rb +51 -0
  33. data/lib/spandx/php/packagist_gateway.rb +1 -1
  34. data/lib/spandx/php/parsers/composer.rb +7 -7
  35. data/lib/spandx/python/parsers/pipfile_lock.rb +4 -4
  36. data/lib/spandx/python/pypi.rb +19 -9
  37. data/lib/spandx/python/source.rb +13 -1
  38. data/lib/spandx/ruby/gateway.rb +1 -1
  39. data/lib/spandx/ruby/parsers/gemfile_lock.rb +10 -9
  40. data/lib/spandx/spdx/catalogue.rb +1 -1
  41. data/lib/spandx/version.rb +1 -1
  42. data/spandx.gemspec +5 -3
  43. metadata +43 -14
  44. data/lib/spandx/core/concurrent.rb +0 -40
  45. data/lib/spandx/core/line_io.rb +0 -23
  46. data/lib/spandx/core/report.rb +0 -60
  47. data/lib/spandx/core/table.rb +0 -29
@@ -27,7 +27,7 @@ module Spandx
27
27
  response = http.get(uri, escape: false)
28
28
 
29
29
  if http.ok?(response)
30
- json = JSON.parse(response.body)
30
+ json = Oj.load(response.body)
31
31
  json['versions'] ? json['versions'][dependency.version] : json
32
32
  else
33
33
  {}
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spandx
4
+ module Os
5
+ module Parsers
6
+ class Apk < ::Spandx::Core::Parser
7
+ def match?(path)
8
+ path.basename.fnmatch?('installed')
9
+ end
10
+
11
+ def parse(lockfile)
12
+ path = lockfile.to_s
13
+
14
+ [].tap do |items|
15
+ lockfile.open(mode: 'r') do |io|
16
+ each_package(io) do |data|
17
+ items.push(map_from(data, path))
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def each_package(io)
26
+ package = {}
27
+
28
+ until io.eof?
29
+ line = io.readline.chomp
30
+ if line.empty?
31
+ yield package
32
+
33
+ package = {}
34
+ else
35
+ line.split(':').tap { |(key, value)| package[key] = value }
36
+ end
37
+ end
38
+ end
39
+
40
+ def map_from(data, path)
41
+ ::Spandx::Core::Dependency.new(
42
+ path: path,
43
+ name: data['P'],
44
+ version: data['V'],
45
+ meta: data.merge('license' => [data['L']])
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -17,7 +17,7 @@ module Spandx
17
17
  response = http.get("https://repo.packagist.org/p/#{dependency.name}.json")
18
18
  return [] unless http.ok?(response)
19
19
 
20
- json = JSON.parse(response.body)
20
+ json = Oj.load(response.body)
21
21
  json['packages'][dependency.name][dependency.version]['license']
22
22
  end
23
23
  end
@@ -4,24 +4,24 @@ module Spandx
4
4
  module Php
5
5
  module Parsers
6
6
  class Composer < ::Spandx::Core::Parser
7
- def matches?(filename)
8
- File.basename(filename) == 'composer.lock'
7
+ def match?(path)
8
+ path.basename.fnmatch? 'composer.lock'
9
9
  end
10
10
 
11
- def parse(file_path)
11
+ def parse(path)
12
12
  items = Set.new
13
- composer_lock = JSON.parse(IO.read(file_path))
13
+ composer_lock = Oj.load(path.read)
14
14
  composer_lock['packages'].concat(composer_lock['packages-dev']).each do |dependency|
15
- items.add(map_from(dependency))
15
+ items.add(map_from(path, dependency))
16
16
  end
17
17
  items
18
18
  end
19
19
 
20
20
  private
21
21
 
22
- def map_from(dependency)
22
+ def map_from(path, dependency)
23
23
  Spandx::Core::Dependency.new(
24
- package_manager: :composer,
24
+ path: path,
25
25
  name: dependency['name'],
26
26
  version: dependency['version'],
27
27
  meta: dependency
@@ -4,8 +4,8 @@ module Spandx
4
4
  module Python
5
5
  module Parsers
6
6
  class PipfileLock < ::Spandx::Core::Parser
7
- def matches?(filename)
8
- filename.match?(/Pipfile.*\.lock/)
7
+ def match?(path)
8
+ path.basename.fnmatch?('Pipfile*.lock')
9
9
  end
10
10
 
11
11
  def parse(lockfile)
@@ -19,10 +19,10 @@ module Spandx
19
19
  private
20
20
 
21
21
  def dependencies_from(lockfile)
22
- json = JSON.parse(IO.read(lockfile))
22
+ json = Oj.load(lockfile.read)
23
23
  each_dependency(json) do |name, version|
24
24
  yield ::Spandx::Core::Dependency.new(
25
- package_manager: :pypi,
25
+ path: lockfile,
26
26
  name: name,
27
27
  version: version,
28
28
  meta: json
@@ -49,19 +49,26 @@ module Spandx
49
49
  end
50
50
 
51
51
  def version_from(url)
52
- path = SUBSTITUTIONS.inject(URI.parse(url).path.split('/')[-1]) do |memo, item|
53
- memo.gsub(item, '')
54
- end
55
-
52
+ path = cleanup(url)
56
53
  return if path.rindex('-').nil?
57
54
 
58
- path.scan(/-\d+\..*/)[-1][1..-1]
55
+ section = path.scan(/-\d+\..*/)
56
+ section = path.scan(/-\d+\.?.*/) if section.empty?
57
+ section[-1][1..-1]
58
+ rescue StandardError => error
59
+ warn([url, error].inspect)
59
60
  end
60
61
 
61
62
  private
62
63
 
63
64
  attr_reader :http
64
65
 
66
+ def cleanup(url)
67
+ SUBSTITUTIONS.inject(URI.parse(url).path.split('/')[-1]) do |memo, item|
68
+ memo.gsub(item, '')
69
+ end
70
+ end
71
+
65
72
  def sources_for(dependency)
66
73
  return default_sources if dependency.meta.empty?
67
74
 
@@ -76,7 +83,7 @@ module Spandx
76
83
  sources.each do |source|
77
84
  html_from(source, '/simple/').css('a[href*="/simple"]').each do |node|
78
85
  each_version(source, node[:href]) do |dependency|
79
- definition = source.lookup(dependency[:name], dependency[:version])
86
+ definition = source.lookup(dependency[:name], dependency[:version], http: http)
80
87
  yield dependency.merge(license: definition['license'])
81
88
  end
82
89
  end
@@ -93,10 +100,13 @@ module Spandx
93
100
 
94
101
  def html_from(source, path)
95
102
  url = URI.join(source.uri.to_s, path).to_s
96
- Nokogiri::HTML(http.get(url).body)
103
+ response = http.get(url)
104
+ if http.ok?(response)
105
+ Nokogiri::HTML(response.body)
106
+ else
107
+ Nokogiri::HTML('<html><head></head><body></body></html>')
108
+ end
97
109
  end
98
110
  end
99
-
100
- PyPI = Pypi
101
111
  end
102
112
  end
@@ -22,17 +22,29 @@ module Spandx
22
22
  def lookup(name, version, http: Spandx.http)
23
23
  response = http.get(uri_for(name, version))
24
24
  if http.ok?(response)
25
- JSON.parse(response.body)
25
+ Oj.load(response.body)
26
26
  else
27
27
  {}
28
28
  end
29
29
  end
30
30
 
31
+ def ==(other)
32
+ name == other.name &&
33
+ uri.to_s == other.uri.to_s &&
34
+ verify_ssl == other.verify_ssl
35
+ end
36
+
37
+ def eql(other)
38
+ self == other
39
+ end
40
+
31
41
  class << self
32
42
  def sources_from(json)
33
43
  meta = json['_meta']
34
44
  meta['sources'].map do |hash|
35
45
  new(hash)
46
+ rescue URI::InvalidURIError
47
+ default
36
48
  end
37
49
  end
38
50
 
@@ -27,7 +27,7 @@ module Spandx
27
27
  end
28
28
 
29
29
  def parse(json)
30
- JSON.parse(json)
30
+ Oj.load(json)
31
31
  end
32
32
  end
33
33
  end
@@ -6,31 +6,32 @@ module Spandx
6
6
  class GemfileLock < ::Spandx::Core::Parser
7
7
  STRIP_BUNDLED_WITH = /^BUNDLED WITH$(\r?\n) (?<major>\d+)\.\d+\.\d+/m.freeze
8
8
 
9
- def matches?(filename)
10
- filename.match?(/Gemfile.*\.lock/) ||
11
- filename.match?(/gems.*\.lock/)
9
+ def match?(pathname)
10
+ basename = pathname.basename
11
+ basename.fnmatch?('Gemfile*.lock') ||
12
+ basename.fnmatch?('gems*.lock')
12
13
  end
13
14
 
14
15
  def parse(lockfile)
15
16
  dependencies_from(lockfile).map do |specification|
16
- map_from(specification)
17
+ map_from(lockfile, specification)
17
18
  end
18
19
  end
19
20
 
20
21
  private
21
22
 
22
23
  def dependencies_from(filepath)
23
- content = IO.read(filepath)
24
- Dir.chdir(File.dirname(filepath)) do
24
+ content = filepath.read.sub(STRIP_BUNDLED_WITH, '')
25
+ Dir.chdir(filepath.dirname) do
25
26
  ::Bundler::LockfileParser
26
- .new(content.sub(STRIP_BUNDLED_WITH, ''))
27
+ .new(content)
27
28
  .specs
28
29
  end
29
30
  end
30
31
 
31
- def map_from(specification)
32
+ def map_from(lockfile, specification)
32
33
  ::Spandx::Core::Dependency.new(
33
- package_manager: :rubygems,
34
+ path: lockfile,
34
35
  name: specification.name,
35
36
  version: specification.version.to_s,
36
37
  meta: {
@@ -33,7 +33,7 @@ module Spandx
33
33
  end
34
34
 
35
35
  def from_file(path)
36
- from_json(IO.read(path))
36
+ from_json(Pathname.new(path).read)
37
37
  end
38
38
 
39
39
  def from_git
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Spandx
4
- VERSION = '0.13.1'
4
+ VERSION = '0.14.0'
5
5
  end
@@ -34,11 +34,13 @@ Gem::Specification.new do |spec|
34
34
 
35
35
  spec.add_dependency 'addressable', '~> 2.7'
36
36
  spec.add_dependency 'bundler', '>= 1.16', '< 3.0.0'
37
- spec.add_dependency 'net-hippie', '~> 0.3'
37
+ spec.add_dependency 'net-hippie', '~> 1.0'
38
38
  spec.add_dependency 'nokogiri', '~> 1.10'
39
+ spec.add_dependency 'oj', '~> 3.10'
39
40
  spec.add_dependency 'parslet', '~> 2.0'
41
+ spec.add_dependency 'terminal-table', '~> 1.8'
40
42
  spec.add_dependency 'thor'
41
- spec.add_dependency 'tty-progressbar', '~> 0.17'
43
+ spec.add_dependency 'tty-spinner', '~> 0.9'
42
44
  spec.add_dependency 'zeitwerk', '~> 2.3'
43
45
 
44
46
  spec.add_development_dependency 'benchmark-ips', '~> 2.8'
@@ -52,6 +54,6 @@ Gem::Specification.new do |spec|
52
54
  spec.add_development_dependency 'rubocop', '~> 0.52'
53
55
  spec.add_development_dependency 'rubocop-rspec', '~> 1.22'
54
56
  spec.add_development_dependency 'ruby-prof', '~> 1.3'
55
- spec.add_development_dependency 'vcr', '~> 5.0'
57
+ spec.add_development_dependency 'vcr', '~> 6.0'
56
58
  spec.add_development_dependency 'webmock', '~> 3.7'
57
59
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spandx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Can Eldem
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-05-17 00:00:00.000000000 Z
12
+ date: 2020-11-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: addressable
@@ -51,14 +51,14 @@ dependencies:
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.3'
54
+ version: '1.0'
55
55
  type: :runtime
56
56
  prerelease: false
57
57
  version_requirements: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.3'
61
+ version: '1.0'
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: nokogiri
64
64
  requirement: !ruby/object:Gem::Requirement
@@ -73,6 +73,20 @@ dependencies:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '1.10'
76
+ - !ruby/object:Gem::Dependency
77
+ name: oj
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.10'
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.10'
76
90
  - !ruby/object:Gem::Dependency
77
91
  name: parslet
78
92
  requirement: !ruby/object:Gem::Requirement
@@ -87,6 +101,20 @@ dependencies:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
103
  version: '2.0'
104
+ - !ruby/object:Gem::Dependency
105
+ name: terminal-table
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.8'
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.8'
90
118
  - !ruby/object:Gem::Dependency
91
119
  name: thor
92
120
  requirement: !ruby/object:Gem::Requirement
@@ -102,19 +130,19 @@ dependencies:
102
130
  - !ruby/object:Gem::Version
103
131
  version: '0'
104
132
  - !ruby/object:Gem::Dependency
105
- name: tty-progressbar
133
+ name: tty-spinner
106
134
  requirement: !ruby/object:Gem::Requirement
107
135
  requirements:
108
136
  - - "~>"
109
137
  - !ruby/object:Gem::Version
110
- version: '0.17'
138
+ version: '0.9'
111
139
  type: :runtime
112
140
  prerelease: false
113
141
  version_requirements: !ruby/object:Gem::Requirement
114
142
  requirements:
115
143
  - - "~>"
116
144
  - !ruby/object:Gem::Version
117
- version: '0.17'
145
+ version: '0.9'
118
146
  - !ruby/object:Gem::Dependency
119
147
  name: zeitwerk
120
148
  requirement: !ruby/object:Gem::Requirement
@@ -289,14 +317,14 @@ dependencies:
289
317
  requirements:
290
318
  - - "~>"
291
319
  - !ruby/object:Gem::Version
292
- version: '5.0'
320
+ version: '6.0'
293
321
  type: :development
294
322
  prerelease: false
295
323
  version_requirements: !ruby/object:Gem::Requirement
296
324
  requirements:
297
325
  - - "~>"
298
326
  - !ruby/object:Gem::Version
299
- version: '5.0'
327
+ version: '6.0'
300
328
  - !ruby/object:Gem::Dependency
301
329
  name: webmock
302
330
  requirement: !ruby/object:Gem::Requirement
@@ -339,9 +367,12 @@ files:
339
367
  - lib/spandx/cli/commands/pull.rb
340
368
  - lib/spandx/cli/commands/scan.rb
341
369
  - lib/spandx/cli/main.rb
370
+ - lib/spandx/cli/printer.rb
371
+ - lib/spandx/cli/printers/csv.rb
372
+ - lib/spandx/cli/printers/json.rb
373
+ - lib/spandx/cli/printers/table.rb
342
374
  - lib/spandx/core/cache.rb
343
375
  - lib/spandx/core/circuit.rb
344
- - lib/spandx/core/concurrent.rb
345
376
  - lib/spandx/core/content.rb
346
377
  - lib/spandx/core/data_file.rb
347
378
  - lib/spandx/core/dependency.rb
@@ -351,15 +382,12 @@ files:
351
382
  - lib/spandx/core/http.rb
352
383
  - lib/spandx/core/index_file.rb
353
384
  - lib/spandx/core/license_plugin.rb
354
- - lib/spandx/core/line_io.rb
355
385
  - lib/spandx/core/parser.rb
356
386
  - lib/spandx/core/path_traversal.rb
357
387
  - lib/spandx/core/plugin.rb
358
388
  - lib/spandx/core/registerable.rb
359
389
  - lib/spandx/core/relation.rb
360
- - lib/spandx/core/report.rb
361
390
  - lib/spandx/core/score.rb
362
- - lib/spandx/core/table.rb
363
391
  - lib/spandx/core/thread_pool.rb
364
392
  - lib/spandx/dotnet/index.rb
365
393
  - lib/spandx/dotnet/nuget_gateway.rb
@@ -376,6 +404,7 @@ files:
376
404
  - lib/spandx/js/parsers/yarn.rb
377
405
  - lib/spandx/js/yarn_lock.rb
378
406
  - lib/spandx/js/yarn_pkg.rb
407
+ - lib/spandx/os/parsers/apk.rb
379
408
  - lib/spandx/php/packagist_gateway.rb
380
409
  - lib/spandx/php/parsers/composer.rb
381
410
  - lib/spandx/python/index.rb
@@ -413,7 +442,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
413
442
  - !ruby/object:Gem::Version
414
443
  version: '0'
415
444
  requirements: []
416
- rubygems_version: 3.1.2
445
+ rubygems_version: 3.1.4
417
446
  signing_key:
418
447
  specification_version: 4
419
448
  summary: A ruby interface to the SPDX catalogue.