spandx 0.13.3 → 0.15.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -2
  3. data/exe/spandx +0 -1
  4. data/ext/spandx/spandx.c +7 -3
  5. data/lib/spandx.rb +1 -1
  6. data/lib/spandx/cli.rb +2 -2
  7. data/lib/spandx/cli/commands/pull.rb +33 -4
  8. data/lib/spandx/cli/commands/scan.rb +19 -22
  9. data/lib/spandx/cli/main.rb +3 -3
  10. data/lib/spandx/cli/printer.rb +27 -0
  11. data/lib/spandx/cli/printers/csv.rb +17 -0
  12. data/lib/spandx/cli/printers/json.rb +17 -0
  13. data/lib/spandx/cli/printers/table.rb +42 -0
  14. data/lib/spandx/core/dependency.rb +48 -13
  15. data/lib/spandx/core/git.rb +6 -6
  16. data/lib/spandx/core/http.rb +6 -6
  17. data/lib/spandx/core/license_plugin.rb +10 -4
  18. data/lib/spandx/core/parser.rb +9 -4
  19. data/lib/spandx/core/path_traversal.rb +4 -13
  20. data/lib/spandx/core/plugin.rb +6 -0
  21. data/lib/spandx/core/thread_pool.rb +49 -0
  22. data/lib/spandx/dotnet/nuget_gateway.rb +1 -1
  23. data/lib/spandx/dotnet/parsers/csproj.rb +7 -7
  24. data/lib/spandx/dotnet/parsers/packages_config.rb +7 -7
  25. data/lib/spandx/dotnet/parsers/sln.rb +10 -13
  26. data/lib/spandx/dotnet/project_file.rb +3 -3
  27. data/lib/spandx/java/parsers/maven.rb +7 -7
  28. data/lib/spandx/js/parsers/npm.rb +8 -8
  29. data/lib/spandx/js/parsers/yarn.rb +7 -7
  30. data/lib/spandx/js/yarn_pkg.rb +1 -1
  31. data/lib/spandx/os/parsers/apk.rb +51 -0
  32. data/lib/spandx/os/parsers/dpkg.rb +69 -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 -7
  37. data/lib/spandx/python/source.rb +1 -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 -4
  43. metadata +38 -20
  44. data/lib/spandx/core/report.rb +0 -60
  45. data/lib/spandx/core/spinner.rb +0 -51
  46. 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
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spandx
4
+ module Os
5
+ module Parsers
6
+ class Dpkg < ::Spandx::Core::Parser
7
+ class LineReader
8
+ attr_reader :io
9
+
10
+ def initialize(io)
11
+ @io = io
12
+ end
13
+
14
+ def each
15
+ yield read_package(io, Hash.new(''), nil) until io.eof?
16
+ end
17
+
18
+ private
19
+
20
+ def read_package(io, package, prev_key)
21
+ return package if io.eof?
22
+
23
+ line = io.readline.chomp
24
+ return package if line.empty?
25
+
26
+ key, value = split(line, prev_key)
27
+ package[key] += value
28
+ read_package(io, package, key)
29
+ end
30
+
31
+ def split(line, prev_key)
32
+ if prev_key && line.start_with?(' ')
33
+ [prev_key, line]
34
+ else
35
+ key, *rest = line.split(':')
36
+ value = rest&.join(':')&.strip
37
+ [key, value]
38
+ end
39
+ end
40
+ end
41
+
42
+ def match?(path)
43
+ path.basename.fnmatch?('status')
44
+ end
45
+
46
+ def parse(lockfile)
47
+ [].tap do |items|
48
+ lockfile.open(mode: 'r') do |io|
49
+ LineReader.new(io).each do |data|
50
+ items.push(map_from(data, lockfile.to_s))
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def map_from(data, path)
59
+ ::Spandx::Core::Dependency.new(
60
+ path: path,
61
+ name: data['Package'],
62
+ version: data['Version'],
63
+ meta: data
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
69
+ 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,7 +100,12 @@ 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
111
  end
@@ -22,7 +22,7 @@ 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
@@ -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.3'
4
+ VERSION = '0.15.1'
5
5
  end
@@ -34,12 +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 'nanospinner', '~> 1.0.0'
38
- spec.add_dependency 'net-hippie', '~> 0.3'
37
+ spec.add_dependency 'net-hippie', '~> 1.0'
39
38
  spec.add_dependency 'nokogiri', '~> 1.10'
39
+ spec.add_dependency 'oj', '~> 3.10'
40
40
  spec.add_dependency 'parslet', '~> 2.0'
41
+ spec.add_dependency 'terminal-table', '~> 1.8'
41
42
  spec.add_dependency 'thor'
42
- spec.add_dependency 'tty-screen', '~> 0.7'
43
+ spec.add_dependency 'tty-spinner', '~> 0.9'
43
44
  spec.add_dependency 'zeitwerk', '~> 2.3'
44
45
 
45
46
  spec.add_development_dependency 'benchmark-ips', '~> 2.8'
@@ -53,6 +54,6 @@ Gem::Specification.new do |spec|
53
54
  spec.add_development_dependency 'rubocop', '~> 0.52'
54
55
  spec.add_development_dependency 'rubocop-rspec', '~> 1.22'
55
56
  spec.add_development_dependency 'ruby-prof', '~> 1.3'
56
- spec.add_development_dependency 'vcr', '~> 5.0'
57
+ spec.add_development_dependency 'vcr', '~> 6.0'
57
58
  spec.add_development_dependency 'webmock', '~> 3.7'
58
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.3
4
+ version: 0.15.1
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-21 00:00:00.000000000 Z
12
+ date: 2020-11-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: addressable
@@ -46,47 +46,47 @@ dependencies:
46
46
  - !ruby/object:Gem::Version
47
47
  version: 3.0.0
48
48
  - !ruby/object:Gem::Dependency
49
- name: nanospinner
49
+ name: net-hippie
50
50
  requirement: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.0.0
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: 1.0.0
61
+ version: '1.0'
62
62
  - !ruby/object:Gem::Dependency
63
- name: net-hippie
63
+ name: nokogiri
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.3'
68
+ version: '1.10'
69
69
  type: :runtime
70
70
  prerelease: false
71
71
  version_requirements: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.3'
75
+ version: '1.10'
76
76
  - !ruby/object:Gem::Dependency
77
- name: nokogiri
77
+ name: oj
78
78
  requirement: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1.10'
82
+ version: '3.10'
83
83
  type: :runtime
84
84
  prerelease: false
85
85
  version_requirements: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1.10'
89
+ version: '3.10'
90
90
  - !ruby/object:Gem::Dependency
91
91
  name: parslet
92
92
  requirement: !ruby/object:Gem::Requirement
@@ -101,6 +101,20 @@ dependencies:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
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'
104
118
  - !ruby/object:Gem::Dependency
105
119
  name: thor
106
120
  requirement: !ruby/object:Gem::Requirement
@@ -116,19 +130,19 @@ dependencies:
116
130
  - !ruby/object:Gem::Version
117
131
  version: '0'
118
132
  - !ruby/object:Gem::Dependency
119
- name: tty-screen
133
+ name: tty-spinner
120
134
  requirement: !ruby/object:Gem::Requirement
121
135
  requirements:
122
136
  - - "~>"
123
137
  - !ruby/object:Gem::Version
124
- version: '0.7'
138
+ version: '0.9'
125
139
  type: :runtime
126
140
  prerelease: false
127
141
  version_requirements: !ruby/object:Gem::Requirement
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: '0.7'
145
+ version: '0.9'
132
146
  - !ruby/object:Gem::Dependency
133
147
  name: zeitwerk
134
148
  requirement: !ruby/object:Gem::Requirement
@@ -303,14 +317,14 @@ dependencies:
303
317
  requirements:
304
318
  - - "~>"
305
319
  - !ruby/object:Gem::Version
306
- version: '5.0'
320
+ version: '6.0'
307
321
  type: :development
308
322
  prerelease: false
309
323
  version_requirements: !ruby/object:Gem::Requirement
310
324
  requirements:
311
325
  - - "~>"
312
326
  - !ruby/object:Gem::Version
313
- version: '5.0'
327
+ version: '6.0'
314
328
  - !ruby/object:Gem::Dependency
315
329
  name: webmock
316
330
  requirement: !ruby/object:Gem::Requirement
@@ -353,6 +367,10 @@ files:
353
367
  - lib/spandx/cli/commands/pull.rb
354
368
  - lib/spandx/cli/commands/scan.rb
355
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
356
374
  - lib/spandx/core/cache.rb
357
375
  - lib/spandx/core/circuit.rb
358
376
  - lib/spandx/core/content.rb
@@ -369,10 +387,8 @@ files:
369
387
  - lib/spandx/core/plugin.rb
370
388
  - lib/spandx/core/registerable.rb
371
389
  - lib/spandx/core/relation.rb
372
- - lib/spandx/core/report.rb
373
390
  - lib/spandx/core/score.rb
374
- - lib/spandx/core/spinner.rb
375
- - lib/spandx/core/table.rb
391
+ - lib/spandx/core/thread_pool.rb
376
392
  - lib/spandx/dotnet/index.rb
377
393
  - lib/spandx/dotnet/nuget_gateway.rb
378
394
  - lib/spandx/dotnet/package_reference.rb
@@ -388,6 +404,8 @@ files:
388
404
  - lib/spandx/js/parsers/yarn.rb
389
405
  - lib/spandx/js/yarn_lock.rb
390
406
  - lib/spandx/js/yarn_pkg.rb
407
+ - lib/spandx/os/parsers/apk.rb
408
+ - lib/spandx/os/parsers/dpkg.rb
391
409
  - lib/spandx/php/packagist_gateway.rb
392
410
  - lib/spandx/php/parsers/composer.rb
393
411
  - lib/spandx/python/index.rb
@@ -425,7 +443,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
425
443
  - !ruby/object:Gem::Version
426
444
  version: '0'
427
445
  requirements: []
428
- rubygems_version: 3.1.3
446
+ rubygems_version: 3.1.4
429
447
  signing_key:
430
448
  specification_version: 4
431
449
  summary: A ruby interface to the SPDX catalogue.