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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -2
- data/exe/spandx +0 -1
- data/ext/spandx/spandx.c +6 -4
- data/lib/spandx.rb +1 -1
- data/lib/spandx/cli.rb +2 -1
- data/lib/spandx/cli/commands/scan.rb +15 -32
- data/lib/spandx/cli/main.rb +3 -3
- data/lib/spandx/cli/printer.rb +27 -0
- data/lib/spandx/cli/printers/csv.rb +17 -0
- data/lib/spandx/cli/printers/json.rb +17 -0
- data/lib/spandx/cli/printers/table.rb +41 -0
- data/lib/spandx/core/dependency.rb +48 -13
- data/lib/spandx/core/git.rb +6 -8
- data/lib/spandx/core/guess.rb +12 -1
- data/lib/spandx/core/http.rb +7 -7
- data/lib/spandx/core/index_file.rb +2 -0
- data/lib/spandx/core/license_plugin.rb +15 -4
- data/lib/spandx/core/parser.rb +10 -3
- data/lib/spandx/core/path_traversal.rb +4 -13
- data/lib/spandx/core/plugin.rb +6 -0
- data/lib/spandx/core/thread_pool.rb +11 -11
- data/lib/spandx/dotnet/nuget_gateway.rb +1 -1
- data/lib/spandx/dotnet/parsers/csproj.rb +7 -7
- data/lib/spandx/dotnet/parsers/packages_config.rb +7 -7
- data/lib/spandx/dotnet/parsers/sln.rb +10 -13
- data/lib/spandx/dotnet/project_file.rb +3 -3
- data/lib/spandx/java/parsers/maven.rb +7 -7
- data/lib/spandx/js/parsers/npm.rb +8 -8
- data/lib/spandx/js/parsers/yarn.rb +7 -7
- data/lib/spandx/js/yarn_pkg.rb +1 -1
- data/lib/spandx/os/parsers/apk.rb +51 -0
- data/lib/spandx/php/packagist_gateway.rb +1 -1
- data/lib/spandx/php/parsers/composer.rb +7 -7
- data/lib/spandx/python/parsers/pipfile_lock.rb +4 -4
- data/lib/spandx/python/pypi.rb +19 -9
- data/lib/spandx/python/source.rb +13 -1
- data/lib/spandx/ruby/gateway.rb +1 -1
- data/lib/spandx/ruby/parsers/gemfile_lock.rb +10 -9
- data/lib/spandx/spdx/catalogue.rb +1 -1
- data/lib/spandx/version.rb +1 -1
- data/spandx.gemspec +5 -3
- metadata +43 -14
- data/lib/spandx/core/concurrent.rb +0 -40
- data/lib/spandx/core/line_io.rb +0 -23
- data/lib/spandx/core/report.rb +0 -60
- data/lib/spandx/core/table.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e01f7023f4a164fb867c7d457769d1c9dd2eb2b480cee88c5d4d682c2d6dc4e
|
4
|
+
data.tar.gz: f202f85c254d11041b79e1305d12641eb66ea66cec9afea25a38e9724a5636d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 926df592dfc76466a7e26bcdfd9fc581957b2c748c9272e30a22180a61f4d6498ebb14e04e8acbbccc813f4aae929ecb8ba82ee54064d642990e5874b05bb0b1
|
7
|
+
data.tar.gz: 5dede807761bf9d4fa91f6a0ea9df1bec9531d0a397fedea5c687b7c12860216e41df47c09fc7e94c8951e7dc0f88c99fc74395913002890b199409b700830f0
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Version 0.
|
1
|
+
Version 0.14.0
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
@@ -9,6 +9,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
9
9
|
|
10
10
|
## [Unreleased]
|
11
11
|
|
12
|
+
## [0.14.0] - 2020-11-14
|
13
|
+
### Added
|
14
|
+
- Parse `/lib/apk/db/installed` file.
|
15
|
+
|
16
|
+
## [0.13.5] - 2020-05-26
|
17
|
+
### Fixed
|
18
|
+
- Process PyPI package urls with single digit versions.
|
19
|
+
- Remove unsupported `hash` report from help text.
|
20
|
+
|
21
|
+
### Changed
|
22
|
+
- Stream output to output stream as soon as results are available.
|
23
|
+
- Switch to `Oj` for JSON parsing.
|
24
|
+
- Run spinner on background thread.
|
25
|
+
|
26
|
+
## [0.13.4] - 2020-05-26
|
27
|
+
### Added
|
28
|
+
- Add detected file path to report output.
|
29
|
+
|
30
|
+
### Changed
|
31
|
+
- Use `Pathname` instead of `String` to represent file paths.
|
32
|
+
- Scan current directory when a path is not specified.
|
33
|
+
|
34
|
+
## [0.13.3] - 2020-05-19
|
35
|
+
### Fixed
|
36
|
+
- Ignore invalid URLs during scan.
|
37
|
+
|
38
|
+
## [0.13.2] - 2020-05-17
|
39
|
+
### Fixed
|
40
|
+
- Detect licenses when provided as an array.
|
41
|
+
- Skip empty lockfiles.
|
42
|
+
|
12
43
|
## [0.13.1] - 2020-05-16
|
13
44
|
### Fixed
|
14
45
|
- Add `ext/**/*.c` and `ext/**/*.h` to list of files.
|
@@ -172,7 +203,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
172
203
|
### Added
|
173
204
|
- Provide ruby API to the latest SPDX catalogue.
|
174
205
|
|
175
|
-
[Unreleased]: https://github.com/spandx/spandx/compare/v0.
|
206
|
+
[Unreleased]: https://github.com/spandx/spandx/compare/v0.14.0...HEAD
|
207
|
+
[0.14.0]: https://github.com/spandx/spandx/compare/v0.13.5...v0.14.0
|
208
|
+
[0.13.5]: https://github.com/spandx/spandx/compare/v0.13.4...v0.13.5
|
209
|
+
[0.13.4]: https://github.com/spandx/spandx/compare/v0.13.3...v0.13.4
|
210
|
+
[0.13.3]: https://github.com/spandx/spandx/compare/v0.13.2...v0.13.3
|
211
|
+
[0.13.2]: https://github.com/spandx/spandx/compare/v0.13.1...v0.13.2
|
212
|
+
[0.13.1]: https://github.com/spandx/spandx/compare/v0.13.0...v0.13.1
|
176
213
|
[0.13.0]: https://github.com/spandx/spandx/compare/v0.12.3...v0.13.0
|
177
214
|
[0.12.3]: https://github.com/spandx/spandx/compare/v0.12.2...v0.12.3
|
178
215
|
[0.12.2]: https://github.com/spandx/spandx/compare/v0.12.1...v0.12.2
|
data/exe/spandx
CHANGED
data/ext/spandx/spandx.c
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
#include "spandx.h"
|
2
2
|
|
3
|
+
#define NEWLINE 10
|
4
|
+
|
3
5
|
VALUE rb_mSpandx;
|
4
6
|
VALUE rb_mCore;
|
5
7
|
VALUE rb_mCsvParser;
|
@@ -9,7 +11,7 @@ VALUE rb_mCsvParser;
|
|
9
11
|
// "name","version","license"\r
|
10
12
|
// "name","version","license"\r\n
|
11
13
|
// "name","version",""\r\n
|
12
|
-
|
14
|
+
VALUE parse(VALUE self, VALUE line)
|
13
15
|
{
|
14
16
|
if (NIL_P(line)) return Qnil;
|
15
17
|
|
@@ -20,7 +22,7 @@ static VALUE parse(VALUE self, VALUE line)
|
|
20
22
|
|
21
23
|
const VALUE items = rb_ary_new2(3);
|
22
24
|
const char *s, *n;
|
23
|
-
const
|
25
|
+
const long len = RSTRING_LEN(line);
|
24
26
|
enum { open, closed } state = closed;
|
25
27
|
|
26
28
|
for (int i = 0; i < len && *p; i++) {
|
@@ -32,13 +34,13 @@ static VALUE parse(VALUE self, VALUE line)
|
|
32
34
|
s = n;
|
33
35
|
state = open;
|
34
36
|
} else if (state == open) {
|
35
|
-
if (!*n || n == p || *n == ',' || *n ==
|
37
|
+
if (!*n || n == p || *n == ',' || *n == NEWLINE) {
|
36
38
|
rb_ary_push(items, rb_str_new(s, p - s));
|
37
39
|
state = closed;
|
38
40
|
}
|
39
41
|
}
|
40
42
|
}
|
41
|
-
*p
|
43
|
+
*(p++);
|
42
44
|
}
|
43
45
|
|
44
46
|
return items;
|
data/lib/spandx.rb
CHANGED
data/lib/spandx/cli.rb
CHANGED
@@ -4,10 +4,7 @@ module Spandx
|
|
4
4
|
module Cli
|
5
5
|
module Commands
|
6
6
|
class Scan
|
7
|
-
|
8
|
-
def advance(*args); end
|
9
|
-
end.new
|
10
|
-
|
7
|
+
include Spandx::Core
|
11
8
|
attr_reader :scan_path
|
12
9
|
|
13
10
|
def initialize(scan_path, options)
|
@@ -17,32 +14,24 @@ module Spandx
|
|
17
14
|
end
|
18
15
|
|
19
16
|
def execute(output: $stdout)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
each_dependency_from(file, pool) do |dependency|
|
24
|
-
report.add(dependency)
|
25
|
-
end
|
17
|
+
with_printer(output) do |printer|
|
18
|
+
each_dependency do |dependency|
|
19
|
+
printer.print_line(Plugin.enhance(dependency), output)
|
26
20
|
end
|
27
|
-
output.puts(format(report.to(@options[:format])))
|
28
21
|
end
|
29
22
|
end
|
30
23
|
|
31
24
|
private
|
32
25
|
|
33
26
|
def each_file
|
34
|
-
|
35
|
-
.new(scan_path, recursive: @options[
|
27
|
+
PathTraversal
|
28
|
+
.new(scan_path, recursive: @options[:recursive])
|
36
29
|
.each { |file| yield file }
|
37
30
|
end
|
38
31
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
::Spandx::Core::Concurrent
|
43
|
-
.map(dependencies, pool: pool) { |dependency| enhance(dependency) }
|
44
|
-
.each do |dependency|
|
45
|
-
bar.advance(1)
|
32
|
+
def each_dependency
|
33
|
+
each_file do |file|
|
34
|
+
Parser.parse(file).each do |dependency|
|
46
35
|
yield dependency
|
47
36
|
end
|
48
37
|
end
|
@@ -52,18 +41,12 @@ module Spandx
|
|
52
41
|
Array(output).map(&:to_s)
|
53
42
|
end
|
54
43
|
|
55
|
-
def
|
56
|
-
::Spandx::
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
def title_for(file)
|
62
|
-
"#{file} [:bar, :elapsed] :percent"
|
63
|
-
end
|
64
|
-
|
65
|
-
def with_progress(title, total)
|
66
|
-
yield @options[:show_progress] ? TTY::ProgressBar.new(title, total: total) : NULL_BAR
|
44
|
+
def with_printer(output)
|
45
|
+
printer = ::Spandx::Cli::Printer.for(@options[:format])
|
46
|
+
printer.print_header(output)
|
47
|
+
yield printer
|
48
|
+
ensure
|
49
|
+
printer.print_footer(output)
|
67
50
|
end
|
68
51
|
end
|
69
52
|
end
|
data/lib/spandx/cli/main.rb
CHANGED
@@ -8,14 +8,14 @@ module Spandx
|
|
8
8
|
method_option :recursive, aliases: '-R', type: :boolean, desc: 'Perform recursive scan', default: false
|
9
9
|
method_option :airgap, aliases: '-a', type: :boolean, desc: 'Disable network connections', default: false
|
10
10
|
method_option :logfile, aliases: '-l', type: :string, desc: 'Path to a logfile', default: '/dev/null'
|
11
|
-
method_option :format, aliases: '-f', type: :string, desc: 'Format of report', default: 'table'
|
11
|
+
method_option :format, aliases: '-f', type: :string, desc: 'Format of report. (table, csv, json)', default: 'table'
|
12
12
|
method_option :pull, aliases: '-p', type: :boolean, desc: 'Pull the latest cache before the scan', default: false
|
13
13
|
method_option :require, aliases: '-r', type: :string, desc: 'Causes spandx to load the library using require.', default: nil
|
14
|
-
|
15
|
-
def scan(lockfile)
|
14
|
+
def scan(lockfile = Pathname.pwd)
|
16
15
|
if options[:help]
|
17
16
|
invoke :help, ['scan']
|
18
17
|
else
|
18
|
+
Oj.default_options = { mode: :strict }
|
19
19
|
Spandx.airgap = options[:airgap]
|
20
20
|
Spandx.logger = Logger.new(options[:logfile])
|
21
21
|
pull if options[:pull]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
class Printer
|
6
|
+
def match?(_format)
|
7
|
+
raise ::Spandx::Error, :match?
|
8
|
+
end
|
9
|
+
|
10
|
+
def print_header(io); end
|
11
|
+
|
12
|
+
def print_line(dependency, io)
|
13
|
+
io.puts(dependency.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def print_footer(io); end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
include Core::Registerable
|
20
|
+
|
21
|
+
def for(format)
|
22
|
+
find { |x| x.match?(format) } || new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
module Printers
|
6
|
+
class Csv < Printer
|
7
|
+
def match?(format)
|
8
|
+
format.to_sym == :csv
|
9
|
+
end
|
10
|
+
|
11
|
+
def print_line(dependency, io)
|
12
|
+
io.puts(CSV.generate_line(dependency.to_a))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
module Printers
|
6
|
+
class Json < Printer
|
7
|
+
def match?(format)
|
8
|
+
format.to_sym == :json
|
9
|
+
end
|
10
|
+
|
11
|
+
def print_line(dependency, io)
|
12
|
+
io.puts(Oj.dump(dependency.to_h))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
module Printers
|
6
|
+
class Table < Printer
|
7
|
+
HEADINGS = ['Name', 'Version', 'Licenses', 'Location'].freeze
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@spinner = TTY::Spinner.new(output: $stderr)
|
11
|
+
end
|
12
|
+
|
13
|
+
def match?(format)
|
14
|
+
format.to_sym == :table
|
15
|
+
end
|
16
|
+
|
17
|
+
def print_header(_io)
|
18
|
+
@dependencies = SortedSet.new
|
19
|
+
@spinner.auto_spin
|
20
|
+
end
|
21
|
+
|
22
|
+
def print_line(dependency, _io)
|
23
|
+
@dependencies << dependency
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_footer(io)
|
27
|
+
@spinner.stop
|
28
|
+
io.puts(to_table(@dependencies.map(&:to_a)))
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def to_table(rows)
|
34
|
+
Terminal::Table.new(headings: HEADINGS) do |table|
|
35
|
+
rows.each { |row| table.add_row(row) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -3,46 +3,81 @@
|
|
3
3
|
module Spandx
|
4
4
|
module Core
|
5
5
|
class Dependency
|
6
|
-
|
6
|
+
PACKAGE_MANAGERS = {
|
7
|
+
Spandx::Dotnet::Parsers::Csproj => :nuget,
|
8
|
+
Spandx::Dotnet::Parsers::PackagesConfig => :nuget,
|
9
|
+
Spandx::Dotnet::Parsers::Sln => :nuget,
|
10
|
+
Spandx::Java::Parsers::Maven => :maven,
|
11
|
+
Spandx::Js::Parsers::Npm => :npm,
|
12
|
+
Spandx::Js::Parsers::Yarn => :yarn,
|
13
|
+
Spandx::Php::Parsers::Composer => :composer,
|
14
|
+
Spandx::Python::Parsers::PipfileLock => :pypi,
|
15
|
+
Spandx::Ruby::Parsers::GemfileLock => :rubygems,
|
16
|
+
Spandx::Os::Parsers::Apk => :apk,
|
17
|
+
}.freeze
|
18
|
+
attr_reader :path, :name, :version, :licenses, :meta
|
7
19
|
|
8
|
-
def initialize(
|
9
|
-
@
|
10
|
-
@name = name
|
11
|
-
@version = version
|
12
|
-
@licenses =
|
20
|
+
def initialize(name:, version:, path:, meta: {})
|
21
|
+
@path = Pathname.new(path).realpath
|
22
|
+
@name = name || @path.basename.to_s
|
23
|
+
@version = version || @path.mtime.to_i.to_s
|
24
|
+
@licenses = []
|
13
25
|
@meta = meta
|
14
26
|
end
|
15
27
|
|
16
|
-
def
|
17
|
-
|
28
|
+
def package_manager
|
29
|
+
PACKAGE_MANAGERS[Parser.for(path).class]
|
18
30
|
end
|
19
31
|
|
20
32
|
def <=>(other)
|
21
|
-
|
33
|
+
return 1 if other.nil?
|
34
|
+
|
35
|
+
score = (name <=> other.name)
|
36
|
+
score = score.zero? ? (version <=> other&.version) : score
|
37
|
+
score.zero? ? (path.to_s <=> other&.path.to_s) : score
|
22
38
|
end
|
23
39
|
|
24
40
|
def hash
|
25
41
|
to_s.hash
|
26
42
|
end
|
27
43
|
|
44
|
+
def ==(other)
|
45
|
+
eql?(other)
|
46
|
+
end
|
47
|
+
|
28
48
|
def eql?(other)
|
29
49
|
to_s == other.to_s
|
30
50
|
end
|
31
51
|
|
32
52
|
def to_s
|
33
|
-
@to_s ||= [name, version].compact.join(' ')
|
53
|
+
@to_s ||= [name, version, path].compact.join(' ')
|
34
54
|
end
|
35
55
|
|
36
56
|
def inspect
|
37
|
-
"
|
57
|
+
"#<#{self.class} name=#{name} version=#{version} path=#{relative_path}>"
|
38
58
|
end
|
39
59
|
|
40
60
|
def to_a
|
41
|
-
[name, version,
|
61
|
+
[name, version, license_expression, relative_path.to_s]
|
42
62
|
end
|
43
63
|
|
44
64
|
def to_h
|
45
|
-
{
|
65
|
+
{
|
66
|
+
name: name,
|
67
|
+
version: version,
|
68
|
+
licenses: license_expression,
|
69
|
+
path: relative_path.to_s
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def relative_path(from: Pathname.pwd)
|
76
|
+
path.relative_path_from(from)
|
77
|
+
end
|
78
|
+
|
79
|
+
def license_expression
|
80
|
+
licenses.map(&:id).join(' AND ')
|
46
81
|
end
|
47
82
|
end
|
48
83
|
end
|
data/lib/spandx/core/git.rb
CHANGED
@@ -11,9 +11,8 @@ module Spandx
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def read(path)
|
14
|
-
full_path =
|
15
|
-
|
16
|
-
IO.read(full_path) if File.exist?(full_path)
|
14
|
+
full_path = root.join(path)
|
15
|
+
full_path.read if full_path.exist?
|
17
16
|
end
|
18
17
|
|
19
18
|
def update!
|
@@ -25,15 +24,16 @@ module Spandx
|
|
25
24
|
def path_for(url)
|
26
25
|
uri = URI.parse(url)
|
27
26
|
name = uri.path.gsub(/\.git$/, '')
|
28
|
-
File.expand_path(File.join(Dir.home, '.local', 'share', name))
|
27
|
+
Pathname(File.expand_path(File.join(Dir.home, '.local', 'share', name)))
|
29
28
|
end
|
30
29
|
|
31
30
|
def dotgit?
|
32
|
-
|
31
|
+
root.join('.git').directory?
|
33
32
|
end
|
34
33
|
|
35
34
|
def clone!
|
36
|
-
system('
|
35
|
+
system('rm', '-rf', root.to_s) if root.exist?
|
36
|
+
system('git', 'clone', '--quiet', '--depth=1', '--single-branch', '--branch', 'master', url, root.to_s)
|
37
37
|
end
|
38
38
|
|
39
39
|
def pull!
|
@@ -42,7 +42,5 @@ module Spandx
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
46
|
-
Database = Git
|
47
45
|
end
|
48
46
|
end
|