spandx 0.5.0 → 0.6.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 +14 -2
- data/lib/spandx/cli/command.rb +65 -0
- data/lib/spandx/cli/commands/index/build.rb +31 -0
- data/lib/spandx/cli/commands/index/update.rb +26 -0
- data/lib/spandx/cli/commands/index.rb +36 -0
- data/lib/spandx/cli/commands/scan.rb +28 -0
- data/lib/spandx/cli.rb +5 -18
- data/lib/spandx/core/content.rb +64 -0
- data/lib/spandx/core/database.rb +65 -0
- data/lib/spandx/core/dependency.rb +23 -0
- data/lib/spandx/core/guess.rb +51 -0
- data/lib/spandx/{parsers/base.rb → core/parser.rb} +16 -2
- data/lib/spandx/core/report.rb +23 -0
- data/lib/spandx/core/score.rb +32 -0
- data/lib/spandx/dotnet/index.rb +72 -0
- data/lib/spandx/{gateways/nuget.rb → dotnet/nuget_gateway.rb} +7 -29
- data/lib/spandx/dotnet/package_reference.rb +21 -0
- data/lib/spandx/dotnet/parsers/csproj.rb +40 -0
- data/lib/spandx/dotnet/parsers/packages_config.rb +39 -0
- data/lib/spandx/dotnet/parsers/sln.rb +47 -0
- data/lib/spandx/dotnet/project_file.rb +50 -0
- data/lib/spandx/java/metadata.rb +47 -0
- data/lib/spandx/java/parsers/maven.rb +44 -0
- data/lib/spandx/parsers/pipfile_lock.rb +2 -2
- data/lib/spandx/{gateways/rubygems.rb → rubygems/gateway.rb} +4 -4
- data/lib/spandx/rubygems/offline_index.rb +72 -0
- data/lib/spandx/rubygems/parsers/gemfile_lock.rb +45 -0
- data/lib/spandx/spdx/catalogue.rb +69 -0
- data/lib/spandx/{gateways/spdx.rb → spdx/gateway.rb} +2 -2
- data/lib/spandx/spdx/license.rb +81 -0
- data/lib/spandx/version.rb +1 -1
- data/lib/spandx.rb +27 -14
- data/spandx.gemspec +2 -2
- metadata +45 -40
- data/lib/spandx/catalogue.rb +0 -67
- data/lib/spandx/command.rb +0 -119
- data/lib/spandx/commands/build.rb +0 -33
- data/lib/spandx/commands/scan.rb +0 -26
- data/lib/spandx/content.rb +0 -62
- data/lib/spandx/database.rb +0 -49
- data/lib/spandx/dependency.rb +0 -21
- data/lib/spandx/guess.rb +0 -76
- data/lib/spandx/index.rb +0 -49
- data/lib/spandx/license.rb +0 -79
- data/lib/spandx/parsers/csproj/package_reference.rb +0 -23
- data/lib/spandx/parsers/csproj/project_file.rb +0 -52
- data/lib/spandx/parsers/csproj.rb +0 -40
- data/lib/spandx/parsers/gemfile_lock.rb +0 -43
- data/lib/spandx/parsers/maven.rb +0 -85
- data/lib/spandx/parsers/packages_config.rb +0 -37
- data/lib/spandx/parsers/sln.rb +0 -45
- data/lib/spandx/parsers.rb +0 -29
- data/lib/spandx/report.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b3a0275ae468012967376aa3bd813b3dc1efef7783eb11d0151d7babf4ac6a8
|
|
4
|
+
data.tar.gz: b86bd82707ad5afa0b83c59674e4f0d08c12733d5f1788038845f30079bb8ba6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f71dd4bf8438ec8857f622076ea345f3c48c8dce3354949459b564ace2c0c002bcc95d09d9d906ba9063bdeb092ade90160f51bdcfb9900a0225114d1aa5aa80
|
|
7
|
+
data.tar.gz: e0bbbdd0156924ac21d9730aa322b332f4d3c1ef43b530f58c56f411c57bfb95c154574bda77c61dda3fbd80b9c8fc9e8a2fea4c09053fa6bfda2c5a6ac86dc5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Version 0.
|
|
1
|
+
Version 0.6.0
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
@@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.6.0] - 2020-03-03
|
|
13
|
+
### Added
|
|
14
|
+
- Add `spandx index update` command to fetch the latest `spandx-rubygems` index.
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
- Drop `spandx-rubygems` dependency.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Pull latest `spandx-rubygems` index via git.
|
|
21
|
+
- Perform binary search on CSV index.
|
|
22
|
+
|
|
12
23
|
## [0.5.0] - 2020-02-13
|
|
13
24
|
### Added
|
|
14
25
|
- Add jaro winkler string similarity support.
|
|
@@ -73,7 +84,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
73
84
|
### Added
|
|
74
85
|
- Provide ruby API to the latest SPDX catalogue.
|
|
75
86
|
|
|
76
|
-
[Unreleased]: https://github.com/mokhan/spandx/compare/v0.
|
|
87
|
+
[Unreleased]: https://github.com/mokhan/spandx/compare/v0.6.0...HEAD
|
|
88
|
+
[0.6.0]: https://github.com/mokhan/spandx/compare/v0.5.0...v0.6.0
|
|
77
89
|
[0.5.0]: https://github.com/mokhan/spandx/compare/v0.4.1...v0.5.0
|
|
78
90
|
[0.4.1]: https://github.com/mokhan/spandx/compare/v0.4.0...v0.4.1
|
|
79
91
|
[0.4.0]: https://github.com/mokhan/spandx/compare/v0.3.0...v0.4.0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Cli
|
|
5
|
+
class Command
|
|
6
|
+
extend Forwardable
|
|
7
|
+
|
|
8
|
+
def_delegators :command, :run
|
|
9
|
+
|
|
10
|
+
def execute(*)
|
|
11
|
+
raise(NotImplementedError, "#{self.class}##{__method__} must be implemented")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def command(**options)
|
|
15
|
+
require 'tty-command'
|
|
16
|
+
TTY::Command.new(options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def cursor
|
|
20
|
+
require 'tty-cursor'
|
|
21
|
+
TTY::Cursor
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def editor
|
|
25
|
+
require 'tty-editor'
|
|
26
|
+
TTY::Editor
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def generator
|
|
30
|
+
require 'tty-file'
|
|
31
|
+
TTY::File
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def pager(**options)
|
|
35
|
+
require 'tty-pager'
|
|
36
|
+
TTY::Pager.new(options)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def platform
|
|
40
|
+
require 'tty-platform'
|
|
41
|
+
TTY::Platform.new
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def prompt(**options)
|
|
45
|
+
require 'tty-prompt'
|
|
46
|
+
TTY::Prompt.new(options)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def screen
|
|
50
|
+
require 'tty-screen'
|
|
51
|
+
TTY::Screen
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def which(*args)
|
|
55
|
+
require 'tty-which'
|
|
56
|
+
TTY::Which.which(*args)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def exec_exist?(*args)
|
|
60
|
+
require 'tty-which'
|
|
61
|
+
TTY::Which.exist?(*args)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Cli
|
|
5
|
+
module Commands
|
|
6
|
+
class Index
|
|
7
|
+
class Build < Spandx::Cli::Command
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def execute(output: $stdout)
|
|
13
|
+
catalogue = Spandx::Spdx::Catalogue.from_git
|
|
14
|
+
indexes.each do |index|
|
|
15
|
+
index.update!(catalogue: catalogue)
|
|
16
|
+
end
|
|
17
|
+
output.puts 'OK'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def indexes
|
|
23
|
+
[
|
|
24
|
+
Spandx::Dotnet::Index.new(directory: @options[:directory])
|
|
25
|
+
]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Cli
|
|
5
|
+
module Commands
|
|
6
|
+
class Index
|
|
7
|
+
class Update < Spandx::Cli::Command
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def execute(output: $stdout)
|
|
13
|
+
[
|
|
14
|
+
'https://github.com/mokhan/spandx-rubygems.git',
|
|
15
|
+
'https://github.com/spdx/license-list-data.git',
|
|
16
|
+
].each do |url|
|
|
17
|
+
output.puts "Updating #{url}..."
|
|
18
|
+
Spandx::Core::Database.new(url: url).update!
|
|
19
|
+
end
|
|
20
|
+
output.puts 'OK'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Cli
|
|
5
|
+
module Commands
|
|
6
|
+
class Index < Thor
|
|
7
|
+
require 'spandx/cli/commands/index/build'
|
|
8
|
+
require 'spandx/cli/commands/index/update'
|
|
9
|
+
|
|
10
|
+
namespace :index
|
|
11
|
+
|
|
12
|
+
desc 'build', 'Build a package index'
|
|
13
|
+
method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
|
|
14
|
+
method_option :directory, aliases: '-d', type: :string, desc: 'Directory to build index in', default: '.index'
|
|
15
|
+
def build(*)
|
|
16
|
+
if options[:help]
|
|
17
|
+
invoke :help, ['build']
|
|
18
|
+
else
|
|
19
|
+
Spandx::Cli::Commands::Index::Build.new(options).execute
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc 'update', 'Update the offline indexes'
|
|
24
|
+
method_option :help, aliases: '-h', type: :boolean,
|
|
25
|
+
desc: 'Display usage information'
|
|
26
|
+
def update(*)
|
|
27
|
+
if options[:help]
|
|
28
|
+
invoke :help, ['update']
|
|
29
|
+
else
|
|
30
|
+
Spandx::Cli::Commands::Index::Update.new(options).execute
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Cli
|
|
5
|
+
module Commands
|
|
6
|
+
class Scan < Spandx::Cli::Command
|
|
7
|
+
attr_reader :lockfile
|
|
8
|
+
|
|
9
|
+
def initialize(lockfile, options)
|
|
10
|
+
@lockfile = lockfile ? ::Pathname.new(File.expand_path(lockfile)) : nil
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def execute(output: $stdout)
|
|
15
|
+
if lockfile.nil?
|
|
16
|
+
output.puts 'OK'
|
|
17
|
+
else
|
|
18
|
+
report = ::Spandx::Core::Report.new
|
|
19
|
+
::Spandx::Core::Parser.for(lockfile).parse(lockfile).each do |dependency|
|
|
20
|
+
report.add(dependency)
|
|
21
|
+
end
|
|
22
|
+
output.puts report.to_json
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/spandx/cli.rb
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
|
-
|
|
5
4
|
require 'spandx'
|
|
6
|
-
require 'spandx/command'
|
|
7
|
-
require 'spandx/commands/
|
|
8
|
-
require 'spandx/commands/scan'
|
|
5
|
+
require 'spandx/cli/command'
|
|
6
|
+
require 'spandx/cli/commands/index'
|
|
7
|
+
require 'spandx/cli/commands/scan'
|
|
9
8
|
|
|
10
9
|
module Spandx
|
|
11
10
|
class CLI < Thor
|
|
@@ -17,19 +16,7 @@ module Spandx
|
|
|
17
16
|
end
|
|
18
17
|
map %w[--version -v] => :version
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
method_option :help, aliases: '-h', type: :boolean,
|
|
22
|
-
desc: 'Display usage information'
|
|
23
|
-
method_option :directory, aliases: '-d', type: :string,
|
|
24
|
-
desc: 'Directory to build index in'
|
|
25
|
-
def build(*)
|
|
26
|
-
if options[:help]
|
|
27
|
-
invoke :help, ['build']
|
|
28
|
-
else
|
|
29
|
-
require_relative 'commands/build'
|
|
30
|
-
Spandx::Commands::Build.new(options).execute
|
|
31
|
-
end
|
|
32
|
-
end
|
|
19
|
+
register Spandx::Cli::Commands::Index, 'index', 'index [SUBCOMMAND]', 'Command description...'
|
|
33
20
|
|
|
34
21
|
desc 'scan LOCKFILE', 'Scan a lockfile and list dependencies/licenses'
|
|
35
22
|
method_option :help, aliases: '-h', type: :boolean,
|
|
@@ -38,7 +25,7 @@ module Spandx
|
|
|
38
25
|
if options[:help]
|
|
39
26
|
invoke :help, ['scan']
|
|
40
27
|
else
|
|
41
|
-
Spandx::Commands::Scan.new(lockfile, options).execute
|
|
28
|
+
Spandx::Cli::Commands::Scan.new(lockfile, options).execute
|
|
42
29
|
end
|
|
43
30
|
end
|
|
44
31
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Core
|
|
5
|
+
class Content
|
|
6
|
+
attr_reader :raw
|
|
7
|
+
|
|
8
|
+
def initialize(raw)
|
|
9
|
+
@raw = raw
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def tokens
|
|
13
|
+
@tokens ||= tokenize(canonicalize(raw)).to_set
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def similar?(other, algorithm: :dice_coefficient)
|
|
17
|
+
case algorithm
|
|
18
|
+
when :dice_coefficient
|
|
19
|
+
similarity_score(other, algorithm: algorithm) > 89.0
|
|
20
|
+
when :levenshtein
|
|
21
|
+
similarity_score(other, algorithm: algorithm) < 3
|
|
22
|
+
when :jaro_winkler
|
|
23
|
+
similarity_score(other, algorithm: algorithm) > 89.0
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def similarity_score(other, algorithm: :dice_coefficient)
|
|
28
|
+
case algorithm
|
|
29
|
+
when :dice_coefficient
|
|
30
|
+
dice_coefficient(other)
|
|
31
|
+
when :levenshtein
|
|
32
|
+
require 'text'
|
|
33
|
+
|
|
34
|
+
Text::Levenshtein.distance(raw, other.raw, 100)
|
|
35
|
+
when :jaro_winkler
|
|
36
|
+
require 'jaro_winkler'
|
|
37
|
+
|
|
38
|
+
JaroWinkler.distance(raw, other.raw) * 100.0
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def canonicalize(content)
|
|
45
|
+
content&.downcase
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def tokenize(content)
|
|
49
|
+
content.to_s.scan(/[a-zA-Z]+/)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def blank?(content)
|
|
53
|
+
content.nil? || content.chomp.strip.empty?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Dice%27s_coefficient#Ruby
|
|
57
|
+
def dice_coefficient(other)
|
|
58
|
+
overlap = (tokens & other.tokens).size
|
|
59
|
+
total = tokens.size + other.tokens.size
|
|
60
|
+
100.0 * (overlap * 2.0 / total)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Core
|
|
5
|
+
class Database
|
|
6
|
+
attr_reader :path, :url
|
|
7
|
+
|
|
8
|
+
def initialize(url:)
|
|
9
|
+
@url = url
|
|
10
|
+
@path = path_for(url)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def update!
|
|
14
|
+
dotgit? ? pull! : clone!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def expand_path(relative_path)
|
|
18
|
+
File.join(path, relative_path)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read(path)
|
|
22
|
+
update! unless dotgit?
|
|
23
|
+
|
|
24
|
+
full_path = expand_path(path)
|
|
25
|
+
IO.read(full_path) if File.exist?(full_path)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def open(path, mode: 'r')
|
|
29
|
+
update! unless dotgit?
|
|
30
|
+
|
|
31
|
+
File.open(expand_path(path), mode) do |io|
|
|
32
|
+
yield io
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def path_for(url)
|
|
39
|
+
uri = URI.parse(url)
|
|
40
|
+
name = uri.path.gsub(/\.git$/, '')
|
|
41
|
+
File.expand_path(File.join(Dir.home, '.local', 'share', name))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def dotgit?
|
|
45
|
+
File.directory?(File.join(path, '.git'))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def clone!
|
|
49
|
+
system('git', 'clone', '--quiet', url, path)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def pull!
|
|
53
|
+
within do
|
|
54
|
+
system('git', 'pull', '--no-rebase', '--quiet', 'origin', 'master')
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def within
|
|
59
|
+
Dir.chdir(path) do
|
|
60
|
+
yield
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Core
|
|
5
|
+
class Dependency
|
|
6
|
+
attr_reader :name, :version, :licenses
|
|
7
|
+
|
|
8
|
+
def initialize(name:, version:, licenses: [])
|
|
9
|
+
@name = name
|
|
10
|
+
@version = version
|
|
11
|
+
@licenses = licenses
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
{
|
|
16
|
+
name: name,
|
|
17
|
+
version: version,
|
|
18
|
+
licenses: licenses.compact.map(&:id)
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Core
|
|
5
|
+
class Guess
|
|
6
|
+
attr_reader :catalogue
|
|
7
|
+
|
|
8
|
+
def initialize(catalogue)
|
|
9
|
+
@catalogue = catalogue
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def license_for(raw_content, algorithm: :dice_coefficient)
|
|
13
|
+
content = Content.new(raw_content)
|
|
14
|
+
score = Score.new(nil, nil)
|
|
15
|
+
threshold = threshold_for(algorithm)
|
|
16
|
+
direction = algorithm == :levenshtein ? method(:min) : method(:max)
|
|
17
|
+
|
|
18
|
+
catalogue.each do |license|
|
|
19
|
+
direction.call(content, license, score, threshold, algorithm) unless license.deprecated_license_id?
|
|
20
|
+
end
|
|
21
|
+
score&.item&.id
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def threshold_for(algorithm)
|
|
27
|
+
{
|
|
28
|
+
dice_coefficient: 89.0,
|
|
29
|
+
jaro_winkler: 80.0,
|
|
30
|
+
levenshtein: 80.0,
|
|
31
|
+
}[algorithm.to_sym]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def min(target, other, score, threshold, algorithm)
|
|
35
|
+
percentage = target.similarity_score(other.content, algorithm: algorithm)
|
|
36
|
+
return if percentage > threshold
|
|
37
|
+
return if score.score > 0.0 && score.score < percentage
|
|
38
|
+
|
|
39
|
+
score.update(percentage, other)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def max(target, other, score, threshold, algorithm)
|
|
43
|
+
percentage = target.similarity_score(other.content, algorithm: algorithm)
|
|
44
|
+
return if percentage < threshold
|
|
45
|
+
return if score.score >= percentage
|
|
46
|
+
|
|
47
|
+
score.update(percentage, other)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Spandx
|
|
4
|
-
module
|
|
5
|
-
class
|
|
4
|
+
module Core
|
|
5
|
+
class Parser
|
|
6
|
+
UNKNOWN = Class.new do
|
|
7
|
+
def self.parse(*_args)
|
|
8
|
+
[]
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
6
12
|
attr_reader :catalogue
|
|
7
13
|
|
|
8
14
|
def initialize(catalogue:)
|
|
@@ -25,6 +31,14 @@ module Spandx
|
|
|
25
31
|
def registry
|
|
26
32
|
@registry ||= []
|
|
27
33
|
end
|
|
34
|
+
|
|
35
|
+
def for(path, catalogue: Spandx::Spdx::Catalogue.from_git)
|
|
36
|
+
result = ::Spandx::Core::Parser.find do |x|
|
|
37
|
+
x.matches?(File.basename(path))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
result&.new(catalogue: catalogue) || UNKNOWN
|
|
41
|
+
end
|
|
28
42
|
end
|
|
29
43
|
end
|
|
30
44
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Core
|
|
5
|
+
class Report
|
|
6
|
+
def initialize(report: { version: '1.0', packages: [] })
|
|
7
|
+
@report = report
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add(dependency)
|
|
11
|
+
@report[:packages].push(dependency.to_h)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
@report
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_json(*_args)
|
|
19
|
+
JSON.pretty_generate(to_h)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Core
|
|
5
|
+
class Score
|
|
6
|
+
include Comparable
|
|
7
|
+
|
|
8
|
+
attr_reader :score, :item
|
|
9
|
+
|
|
10
|
+
def initialize(score, item)
|
|
11
|
+
update(score || 0.0, item)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def update(score, item)
|
|
15
|
+
@score = score
|
|
16
|
+
@item = item
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def empty?
|
|
20
|
+
score.nil? || item.nil?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def <=>(other)
|
|
24
|
+
score <=> other.score
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_s
|
|
28
|
+
"#{score}: #{item}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spandx
|
|
4
|
+
module Dotnet
|
|
5
|
+
class Index
|
|
6
|
+
DEFAULT_DIR = File.expand_path(File.join(Dir.home, '.local', 'share', 'spandx'))
|
|
7
|
+
attr_reader :directory
|
|
8
|
+
|
|
9
|
+
def initialize(directory: DEFAULT_DIR)
|
|
10
|
+
@directory = directory ? File.expand_path(directory) : DEFAULT_DIR
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def update!(catalogue:, limit: nil)
|
|
14
|
+
counter = 0
|
|
15
|
+
gateway = Spandx::Dotnet::NugetGateway.new(catalogue: catalogue)
|
|
16
|
+
gateway.each do |spec|
|
|
17
|
+
next unless spec['licenseExpression']
|
|
18
|
+
|
|
19
|
+
write([gateway.host, spec['id'], spec['version']], spec['licenseExpression'])
|
|
20
|
+
|
|
21
|
+
if limit
|
|
22
|
+
counter += 1
|
|
23
|
+
break if counter > limit
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def indexed?(key)
|
|
29
|
+
File.exist?(data_file_for(digest_for(key)))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def read(key)
|
|
33
|
+
open_data(digest_for(key), mode: 'r', &:read)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def write(key, data)
|
|
37
|
+
return if data.nil? || data.empty?
|
|
38
|
+
|
|
39
|
+
open_data(digest_for(key)) do |x|
|
|
40
|
+
x.write(data)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def digest_for(components)
|
|
47
|
+
Digest::SHA1.hexdigest(Array(components).join('/'))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def open_data(key, mode: 'w')
|
|
51
|
+
FileUtils.mkdir_p(data_dir_for(key))
|
|
52
|
+
File.open(data_file_for(key), mode) do |file|
|
|
53
|
+
yield file
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def data_dir_for(index_key)
|
|
58
|
+
File.join(directory, *index_key.scan(/../)).downcase
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def data_file_for(key)
|
|
62
|
+
File.join(data_dir_for(key), 'data')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def upsert!(spec)
|
|
66
|
+
return unless spec['licenseExpression']
|
|
67
|
+
|
|
68
|
+
write([host, spec['id'], spec['version']], spec['licenseExpression'])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|