spandx 0.13.0 → 0.13.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -2
- data/exe/spandx +0 -1
- data/ext/spandx/spandx.c +55 -0
- data/ext/spandx/spandx.h +6 -0
- 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 +47 -13
- data/lib/spandx/core/git.rb +6 -8
- data/lib/spandx/core/guess.rb +12 -1
- data/lib/spandx/core/http.rb +1 -1
- data/lib/spandx/core/index_file.rb +2 -0
- data/lib/spandx/core/license_plugin.rb +14 -3
- 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/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 -2
- metadata +42 -12
- 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: 01abc42f6e315aee9f35bf60cdad7a4801ee95ae4a186ef3ee001f2617c9891e
|
4
|
+
data.tar.gz: 78248675cdddbcb197f347239c85016862254a113b17894e4d6ffe7ecd33cddd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6d4462c74dc412f9016ff576f55e67bdc9a6b341059d3b372b505f6e7ee730a92da53a4b5d0ab836df298b2cb527d0890c599fdc48f92a848b3d93c6c7d67ab
|
7
|
+
data.tar.gz: fdc618b97c619aa7d8a99b799dc5b1569d8396e9cfbbee5ee91cf7b994335e7fcbd9a5abac03b87a21615b23eef7913d5b59aabf277cacaa3eeac8497d795f38
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Version 0.13.
|
1
|
+
Version 0.13.5
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
@@ -8,6 +8,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
8
8
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
9
9
|
|
10
10
|
## [Unreleased]
|
11
|
+
## [0.13.5] - 2020-05-26
|
12
|
+
### Fixed
|
13
|
+
- Process PyPI package urls with single digit versions.
|
14
|
+
- Remove unsupported `hash` report from help text.
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
- Stream output to output stream as soon as results are available.
|
18
|
+
- Switch to `Oj` for JSON parsing.
|
19
|
+
- Run spinner on background thread.
|
20
|
+
|
21
|
+
## [0.13.4] - 2020-05-26
|
22
|
+
### Added
|
23
|
+
- Add detected file path to report output.
|
24
|
+
|
25
|
+
### Changed
|
26
|
+
- Use `Pathname` instead of `String` to represent file paths.
|
27
|
+
- Scan current directory when a path is not specified.
|
28
|
+
|
29
|
+
## [0.13.3] - 2020-05-19
|
30
|
+
### Fixed
|
31
|
+
- Ignore invalid URLs during scan.
|
32
|
+
|
33
|
+
## [0.13.2] - 2020-05-17
|
34
|
+
### Fixed
|
35
|
+
- Detect licenses when provided as an array.
|
36
|
+
- Skip empty lockfiles.
|
37
|
+
|
38
|
+
## [0.13.1] - 2020-05-16
|
39
|
+
### Fixed
|
40
|
+
- Add `ext/**/*.c` and `ext/**/*.h` to list of files.
|
11
41
|
|
12
42
|
## [0.13.0] - 2020-05-12
|
13
43
|
### Added
|
@@ -168,7 +198,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
168
198
|
### Added
|
169
199
|
- Provide ruby API to the latest SPDX catalogue.
|
170
200
|
|
171
|
-
[Unreleased]: https://github.com/spandx/spandx/compare/v0.13.
|
201
|
+
[Unreleased]: https://github.com/spandx/spandx/compare/v0.13.5...HEAD
|
202
|
+
[0.13.5]: https://github.com/spandx/spandx/compare/v0.13.4...v0.13.5
|
203
|
+
[0.13.4]: https://github.com/spandx/spandx/compare/v0.13.3...v0.13.4
|
204
|
+
[0.13.3]: https://github.com/spandx/spandx/compare/v0.13.2...v0.13.3
|
205
|
+
[0.13.2]: https://github.com/spandx/spandx/compare/v0.13.1...v0.13.2
|
206
|
+
[0.13.1]: https://github.com/spandx/spandx/compare/v0.13.0...v0.13.1
|
172
207
|
[0.13.0]: https://github.com/spandx/spandx/compare/v0.12.3...v0.13.0
|
173
208
|
[0.12.3]: https://github.com/spandx/spandx/compare/v0.12.2...v0.12.3
|
174
209
|
[0.12.2]: https://github.com/spandx/spandx/compare/v0.12.1...v0.12.2
|
data/exe/spandx
CHANGED
data/ext/spandx/spandx.c
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#include "spandx.h"
|
2
|
+
|
3
|
+
#define NEWLINE 10
|
4
|
+
|
5
|
+
VALUE rb_mSpandx;
|
6
|
+
VALUE rb_mCore;
|
7
|
+
VALUE rb_mCsvParser;
|
8
|
+
|
9
|
+
// "name","version","license"
|
10
|
+
// "name","version","license"\n
|
11
|
+
// "name","version","license"\r
|
12
|
+
// "name","version","license"\r\n
|
13
|
+
// "name","version",""\r\n
|
14
|
+
VALUE parse(VALUE self, VALUE line)
|
15
|
+
{
|
16
|
+
if (NIL_P(line)) return Qnil;
|
17
|
+
|
18
|
+
char *p;
|
19
|
+
|
20
|
+
p = RSTRING_PTR(line);
|
21
|
+
if (*p != '"') return Qnil;
|
22
|
+
|
23
|
+
const VALUE items = rb_ary_new2(3);
|
24
|
+
const char *s, *n;
|
25
|
+
const long len = RSTRING_LEN(line);
|
26
|
+
enum { open, closed } state = closed;
|
27
|
+
|
28
|
+
for (int i = 0; i < len && *p; i++) {
|
29
|
+
if (*p == '"') {
|
30
|
+
n = p;
|
31
|
+
if (i < (len - 1)) *n++;
|
32
|
+
|
33
|
+
if (state == closed) {
|
34
|
+
s = n;
|
35
|
+
state = open;
|
36
|
+
} else if (state == open) {
|
37
|
+
if (!*n || n == p || *n == ',' || *n == NEWLINE) {
|
38
|
+
rb_ary_push(items, rb_str_new(s, p - s));
|
39
|
+
state = closed;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
*(p++);
|
44
|
+
}
|
45
|
+
|
46
|
+
return items;
|
47
|
+
}
|
48
|
+
|
49
|
+
void Init_spandx(void)
|
50
|
+
{
|
51
|
+
rb_mSpandx = rb_define_module("Spandx");
|
52
|
+
rb_mCore = rb_define_module_under(rb_mSpandx, "Core");
|
53
|
+
rb_mCsvParser = rb_define_module_under(rb_mCore, "CsvParser");
|
54
|
+
rb_define_module_function(rb_mCsvParser, "parse", parse, 1);
|
55
|
+
}
|
data/ext/spandx/spandx.h
ADDED
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,80 @@
|
|
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
|
+
}.freeze
|
17
|
+
attr_reader :path, :name, :version, :licenses, :meta
|
7
18
|
|
8
|
-
def initialize(
|
9
|
-
@
|
10
|
-
@name = name
|
11
|
-
@version = version
|
12
|
-
@licenses =
|
19
|
+
def initialize(name:, version:, path:, meta: {})
|
20
|
+
@path = Pathname.new(path).realpath
|
21
|
+
@name = name || @path.basename.to_s
|
22
|
+
@version = version || @path.mtime.to_i.to_s
|
23
|
+
@licenses = []
|
13
24
|
@meta = meta
|
14
25
|
end
|
15
26
|
|
16
|
-
def
|
17
|
-
|
27
|
+
def package_manager
|
28
|
+
PACKAGE_MANAGERS[Parser.for(path).class]
|
18
29
|
end
|
19
30
|
|
20
31
|
def <=>(other)
|
21
|
-
|
32
|
+
return 1 if other.nil?
|
33
|
+
|
34
|
+
score = (name <=> other.name)
|
35
|
+
score = score.zero? ? (version <=> other&.version) : score
|
36
|
+
score.zero? ? (path.to_s <=> other&.path.to_s) : score
|
22
37
|
end
|
23
38
|
|
24
39
|
def hash
|
25
40
|
to_s.hash
|
26
41
|
end
|
27
42
|
|
43
|
+
def ==(other)
|
44
|
+
eql?(other)
|
45
|
+
end
|
46
|
+
|
28
47
|
def eql?(other)
|
29
48
|
to_s == other.to_s
|
30
49
|
end
|
31
50
|
|
32
51
|
def to_s
|
33
|
-
@to_s ||= [name, version].compact.join(' ')
|
52
|
+
@to_s ||= [name, version, path].compact.join(' ')
|
34
53
|
end
|
35
54
|
|
36
55
|
def inspect
|
37
|
-
"
|
56
|
+
"#<#{self.class} name=#{name} version=#{version} path=#{relative_path}>"
|
38
57
|
end
|
39
58
|
|
40
59
|
def to_a
|
41
|
-
[name, version,
|
60
|
+
[name, version, license_expression, relative_path.to_s]
|
42
61
|
end
|
43
62
|
|
44
63
|
def to_h
|
45
|
-
{
|
64
|
+
{
|
65
|
+
name: name,
|
66
|
+
version: version,
|
67
|
+
licenses: license_expression,
|
68
|
+
path: relative_path.to_s
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def relative_path(from: Pathname.pwd)
|
75
|
+
path.relative_path_from(from)
|
76
|
+
end
|
77
|
+
|
78
|
+
def license_expression
|
79
|
+
licenses.map(&:id).join(' AND ')
|
46
80
|
end
|
47
81
|
end
|
48
82
|
end
|