package-audit 0.1.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 +7 -0
- data/exe/package-audit +10 -0
- data/lib/package/audit/cli.rb +134 -0
- data/lib/package/audit/const.rb +5 -0
- data/lib/package/audit/dependency.rb +57 -0
- data/lib/package/audit/dependency_printer.rb +128 -0
- data/lib/package/audit/enum/environment.rb +15 -0
- data/lib/package/audit/enum/risk_explanation.rb +14 -0
- data/lib/package/audit/enum/risk_type.rb +12 -0
- data/lib/package/audit/enum/vulnerability_type.rb +14 -0
- data/lib/package/audit/formatter/base.rb +11 -0
- data/lib/package/audit/formatter/risk.rb +28 -0
- data/lib/package/audit/formatter/version.rb +33 -0
- data/lib/package/audit/formatter/version_date.rb +28 -0
- data/lib/package/audit/formatter/vulnerability.rb +37 -0
- data/lib/package/audit/risk.rb +27 -0
- data/lib/package/audit/risk_calculator.rb +65 -0
- data/lib/package/audit/ruby/bundler_specs.rb +28 -0
- data/lib/package/audit/ruby/gem_collection.rb +43 -0
- data/lib/package/audit/ruby/gem_meta_data.rb +58 -0
- data/lib/package/audit/ruby/vulnerability_finder.rb +24 -0
- data/lib/package/audit/util/bash_color.rb +35 -0
- data/lib/package/audit/util/summary_printer.rb +75 -0
- data/lib/package/audit/version.rb +5 -0
- data/sig/const.rbs +5 -0
- data/sig/package/audit/cli.rbs +31 -0
- data/sig/package/audit/dependency.rbs +35 -0
- data/sig/package/audit/dependency_printer.rbs +24 -0
- data/sig/package/audit/enum/environment.rbs +13 -0
- data/sig/package/audit/enum/risk_explanation.rbs +12 -0
- data/sig/package/audit/enum/risk_type.rbs +12 -0
- data/sig/package/audit/enum/vulnerability_type.rbs +14 -0
- data/sig/package/audit/formatter/base.rbs +9 -0
- data/sig/package/audit/formatter/risk_printer.rbs +13 -0
- data/sig/package/audit/formatter/version_date.rbs +13 -0
- data/sig/package/audit/formatter/version_printer.rbs +14 -0
- data/sig/package/audit/formatter/vulnerability.rbs +13 -0
- data/sig/package/audit/risk.rbs +12 -0
- data/sig/package/audit/risk_calculator.rbs +21 -0
- data/sig/package/audit/ruby/bundler_specs.rbs +11 -0
- data/sig/package/audit/ruby/gem_collection.rbs +15 -0
- data/sig/package/audit/ruby/gem_meta_data.rbs +23 -0
- data/sig/package/audit/ruby/vulnerability_finder.rbs +9 -0
- data/sig/package/audit/util/bash_color.rbs +21 -0
- data/sig/package/audit/util/summary_printer.rbs +21 -0
- data/sig/package/audit/version.rbs +5 -0
- metadata +121 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative './bundler_specs'
|
2
|
+
require_relative './../enum/risk_type'
|
3
|
+
|
4
|
+
module Package
|
5
|
+
module Audit
|
6
|
+
module Ruby
|
7
|
+
class GemCollection
|
8
|
+
def self.all
|
9
|
+
specs = BundlerSpecs.gemfile
|
10
|
+
dependencies = specs.map { |spec| Dependency.new(spec.name, spec.version) }
|
11
|
+
vulnerable_deps = VulnerabilityFinder.run
|
12
|
+
GemMetaData.new(dependencies + vulnerable_deps).fetch.filter(&:risk?).sort_by(&:name).uniq(&:name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.deprecated
|
16
|
+
specs = BundlerSpecs.gemfile
|
17
|
+
dependencies = specs.map { |spec| Dependency.new(spec.name, spec.version) }
|
18
|
+
|
19
|
+
GemMetaData.new(dependencies).fetch.filter do |dep|
|
20
|
+
dep.risk.explanation == Enum::RiskExplanation::POTENTIAL_DEPRECATION
|
21
|
+
end.sort_by(&:name).uniq(&:name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.outdated(include_implicit: false)
|
25
|
+
specs = include_implicit ? BundlerSpecs.all : BundlerSpecs.gemfile
|
26
|
+
dependencies = specs.map { |spec| Dependency.new(spec.name, spec.version) }
|
27
|
+
|
28
|
+
GemMetaData.new(dependencies).fetch.filter do |dep|
|
29
|
+
dep.version < dep.latest_version
|
30
|
+
end.sort_by(&:name).uniq(&:name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.vulnerable
|
34
|
+
dependencies = VulnerabilityFinder.run
|
35
|
+
|
36
|
+
GemMetaData.new(dependencies).fetch.filter do |dep|
|
37
|
+
dep.risk.explanation == Enum::RiskExplanation::VULNERABILITY
|
38
|
+
end.sort_by(&:name).uniq(&:name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative '../dependency'
|
2
|
+
|
3
|
+
module Package
|
4
|
+
module Audit
|
5
|
+
module Ruby
|
6
|
+
class GemMetaData
|
7
|
+
def initialize(dependencies)
|
8
|
+
@dependencies = dependencies
|
9
|
+
@gem_hash = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch
|
13
|
+
find_rubygems_metadata
|
14
|
+
assign_groups
|
15
|
+
@gem_hash.values
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def find_rubygems_metadata # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
21
|
+
fetcher = Gem::SpecFetcher.fetcher
|
22
|
+
|
23
|
+
@dependencies.each do |dep|
|
24
|
+
gem_dependency = Gem::Dependency.new dep.name, ">= #{dep.version}"
|
25
|
+
local_version_date = Time.new(0)
|
26
|
+
latest_version_date = Time.new(0)
|
27
|
+
local_version = Gem::Version.new(dep.version)
|
28
|
+
latest_version = Gem::Version.new('0.0.0')
|
29
|
+
|
30
|
+
remote_dependencies, = fetcher.spec_for_dependency gem_dependency
|
31
|
+
|
32
|
+
remote_dependencies.each do |remote_spec, _|
|
33
|
+
latest_version = remote_spec.version if latest_version < remote_spec.version
|
34
|
+
latest_version_date = remote_spec.date if latest_version_date < remote_spec.date
|
35
|
+
local_version_date = remote_spec.date if local_version == remote_spec.version
|
36
|
+
end
|
37
|
+
|
38
|
+
@gem_hash[dep.name] = dep
|
39
|
+
dep.update latest_version: latest_version.to_s,
|
40
|
+
version_date: local_version_date.strftime('%Y-%m-%d'),
|
41
|
+
latest_version_date: latest_version_date.strftime('%Y-%m-%d')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def assign_groups # rubocop:disable Metrics/AbcSize
|
46
|
+
definition = Bundler.definition
|
47
|
+
groups = definition.groups.uniq.sort
|
48
|
+
groups.each do |group|
|
49
|
+
specs = definition.specs_for([group])
|
50
|
+
specs.each do |spec|
|
51
|
+
@gem_hash[spec.name].update(groups: @gem_hash[spec.name].groups | [group]) if @gem_hash.key? spec.name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative '../enum/vulnerability_type'
|
2
|
+
|
3
|
+
module Package
|
4
|
+
module Audit
|
5
|
+
module Ruby
|
6
|
+
class VulnerabilityFinder
|
7
|
+
def self.run # rubocop:disable Metrics/AbcSize
|
8
|
+
gem_hash = {}
|
9
|
+
json_str = `bundle exec bundle-audit check --update --quiet --format json`
|
10
|
+
json_results = JSON.parse(json_str, symbolize_names: true)[:results]
|
11
|
+
json_results.each do |result|
|
12
|
+
gem_name = result[:gem][:name]
|
13
|
+
vulnerability = result[:advisory][:criticality] || Enum::VulnerabilityType::UNKNOWN
|
14
|
+
unless gem_hash.key? gem_name
|
15
|
+
gem_hash[gem_name] = Dependency.new result[:gem][:name], result[:gem][:version]
|
16
|
+
end
|
17
|
+
gem_hash[gem_name].update vulnerabilities: gem_hash[gem_name].vulnerabilities + [vulnerability]
|
18
|
+
end
|
19
|
+
gem_hash.values
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
module Util
|
4
|
+
module BashColor
|
5
|
+
def self.green(str)
|
6
|
+
"\e[32m#{str}\e[0m"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.yellow(str)
|
10
|
+
"\e[33m#{str}\e[0m"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.orange(str)
|
14
|
+
"\e[38;5;208m#{str}\e[0m"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.red(str)
|
18
|
+
"\e[31m#{str}\e[0m"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.cyan(str)
|
22
|
+
"\e[36m#{str}\e[0m"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.magenta(str)
|
26
|
+
"\e[35m#{str}\e[0m"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.blue(str)
|
30
|
+
"\e[34m#{str}\e[0m"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative '../const'
|
2
|
+
require_relative './bash_color'
|
3
|
+
|
4
|
+
module Package
|
5
|
+
module Audit
|
6
|
+
module Util
|
7
|
+
module SummaryPrinter
|
8
|
+
def self.report
|
9
|
+
printf("\n%<info>s\n%<cmd>s\n\n",
|
10
|
+
info: Util::BashColor.blue('To show how risk is calculated run:'),
|
11
|
+
cmd: Util::BashColor.magenta(' > bundle exec package-audit risk'))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.deprecated
|
15
|
+
puts Util::BashColor.blue("\nAlthough gems listed above have no recent updates, they may not be deprecated.")
|
16
|
+
puts Util::BashColor.blue("Please contact the gem author for more information about its status.\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.outdated
|
20
|
+
printf("\n%<info>s\n%<cmd>s\n\n",
|
21
|
+
info: Util::BashColor.blue('To show both Gemfile gems and their dependencies run:'),
|
22
|
+
cmd: Util::BashColor.magenta(' > bundle exec package-audit outdated --include-implicit'))
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.vulnerable
|
26
|
+
printf("\n%<info>s\n%<cmd>s\n\n",
|
27
|
+
info: Util::BashColor.blue('To get more information about the vulnerabilities run:'),
|
28
|
+
cmd: Util::BashColor.magenta(' > bundle exec bundle-audit check --update'))
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.total(num)
|
32
|
+
puts Util::BashColor.cyan("\nFound a total of #{num} gems.")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.risk # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
36
|
+
puts Util::BashColor.blue('1. Check if the dependency has a security vulnerability.')
|
37
|
+
puts ' If yes, the following vulnerability -> risk mapping is used:'
|
38
|
+
puts " - #{Util::BashColor.red('unknown')} vulnerability\t-> #{Util::BashColor.red('high')} risk"
|
39
|
+
puts " - #{Util::BashColor.red('critical')} vulnerability\t-> #{Util::BashColor.red('high')} risk"
|
40
|
+
puts " - #{Util::BashColor.red('high')} vulnerability\t-> #{Util::BashColor.red('high')} risk"
|
41
|
+
puts " - #{Util::BashColor.orange('medium')} vulnerability\t-> #{Util::BashColor.orange('medium')} risk"
|
42
|
+
puts " - #{Util::BashColor.yellow('low')} vulnerability\t-> #{Util::BashColor.yellow('low')} risk"
|
43
|
+
|
44
|
+
puts
|
45
|
+
|
46
|
+
puts Util::BashColor.blue('2. Check the dependency for potential deprecation.')
|
47
|
+
puts " If no new releases by author for at least #{Const::YEARS_ELAPSED_TO_BE_OUTDATED} years:"
|
48
|
+
puts " - assign the risk to\t-> #{Util::BashColor.orange('medium')} risk"
|
49
|
+
|
50
|
+
puts
|
51
|
+
|
52
|
+
puts Util::BashColor.blue('3. Check if a newer version of the dependency is available.')
|
53
|
+
|
54
|
+
puts ' If yes, assign risk as follows:'
|
55
|
+
puts " - #{Util::BashColor.orange('major version')} mismatch\t-> #{Util::BashColor.orange('medium')} risk" # rubocop:disable Layout/LineLength
|
56
|
+
puts " - #{Util::BashColor.yellow('minor version')} mismatch\t-> #{Util::BashColor.yellow('low')} risk"
|
57
|
+
puts " - #{Util::BashColor.green('patch version')} mismatch\t-> #{Util::BashColor.yellow('low')} risk"
|
58
|
+
puts " - #{Util::BashColor.green('build version')} mismatch\t-> #{Util::BashColor.yellow('low')} risk"
|
59
|
+
|
60
|
+
puts
|
61
|
+
|
62
|
+
puts Util::BashColor.blue('4. Take the highest risk from the first 3 steps.')
|
63
|
+
puts ' If two risks match in severity, use the following precedence:'
|
64
|
+
puts " - #{Util::BashColor.red('vulnerability')} > #{Util::BashColor.orange('deprecation')} > #{Util::BashColor.yellow('outdatedness')}" # rubocop:disable Layout/LineLength
|
65
|
+
|
66
|
+
puts
|
67
|
+
|
68
|
+
puts Util::BashColor.blue('5. Check whether the dependency is used in production or not.')
|
69
|
+
puts ' If a dependency is limited to a non-production environment:'
|
70
|
+
puts " - cap risk severity to\t -> #{Util::BashColor.orange('medium')} risk"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/sig/const.rbs
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
class CLI
|
4
|
+
def self.exit_on_failure?: -> bool
|
5
|
+
|
6
|
+
def deprecated: (String) -> void
|
7
|
+
|
8
|
+
def outdated: (String) -> void
|
9
|
+
|
10
|
+
def report: (String) -> void
|
11
|
+
|
12
|
+
def risk: -> void
|
13
|
+
|
14
|
+
def version: -> void
|
15
|
+
|
16
|
+
def vulnerable: (String) -> void
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def exit_with_error: (String) -> void
|
21
|
+
|
22
|
+
def exit_with_success: (String) -> void
|
23
|
+
|
24
|
+
def print_total: (Integer) -> void
|
25
|
+
|
26
|
+
def print_vulnerability_info: (String) -> void
|
27
|
+
|
28
|
+
def within_rescue_block: (String) { () -> void } -> void
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
class Dependency
|
4
|
+
@groups: Array[Symbol]
|
5
|
+
@risk: Risk
|
6
|
+
@vulnerabilities: Array[String]
|
7
|
+
|
8
|
+
attr_accessor groups: Array[Symbol]
|
9
|
+
attr_accessor latest_version: String
|
10
|
+
attr_accessor latest_version_date: String
|
11
|
+
attr_reader name: String
|
12
|
+
attr_reader version: String
|
13
|
+
attr_accessor version_date: String
|
14
|
+
attr_accessor vulnerabilities: Array[String]
|
15
|
+
|
16
|
+
def initialize: (String, String) -> void
|
17
|
+
|
18
|
+
def group_list: -> String
|
19
|
+
|
20
|
+
def risk?: -> bool
|
21
|
+
|
22
|
+
def risk: -> Risk
|
23
|
+
|
24
|
+
def risk_explanation: -> String?
|
25
|
+
|
26
|
+
def risk_type: -> String
|
27
|
+
|
28
|
+
def to_csv: (Array[Symbol]) -> String
|
29
|
+
|
30
|
+
def update: (**untyped) -> void
|
31
|
+
|
32
|
+
def vulnerabilities_grouped: -> String
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
class DependencyPrinter
|
4
|
+
BASH_FORMATTING_REGEX: Regexp
|
5
|
+
COLUMN_GAP: Integer
|
6
|
+
CSV_HEADERS: Hash[Symbol, String]
|
7
|
+
FIELDS: Array[Symbol]
|
8
|
+
HEADERS: Hash[Symbol, String]
|
9
|
+
|
10
|
+
@dependencies: Array[Dependency]
|
11
|
+
@options: Hash[Symbol, untyped]
|
12
|
+
|
13
|
+
def initialize: (Array[Dependency], Hash[Symbol, untyped]) -> void
|
14
|
+
|
15
|
+
def print: (?Array[Symbol]) -> void
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def csv: (Array[Symbol], ?exclude_headers: bool) -> void
|
20
|
+
|
21
|
+
def pretty: (Array[Symbol]) -> void
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
class RiskCalculator
|
4
|
+
@dependency: Dependency
|
5
|
+
|
6
|
+
def initialize: (Dependency) -> void
|
7
|
+
|
8
|
+
def find: -> Risk?
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def assess_deprecation_risk: -> Risk
|
13
|
+
|
14
|
+
def assess_version_risk: -> Risk
|
15
|
+
|
16
|
+
def assess_vulnerability_risk: -> Risk
|
17
|
+
|
18
|
+
def production_dependency?: -> bool
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
module Ruby
|
4
|
+
class GemCollection
|
5
|
+
def self.all: -> Array[Dependency]
|
6
|
+
|
7
|
+
def self.deprecated: -> Array[Dependency]
|
8
|
+
|
9
|
+
def self.outdated: (?include_implicit: bool) -> Array[Dependency]
|
10
|
+
|
11
|
+
def self.vulnerable: -> Array[Dependency]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
module Ruby
|
4
|
+
class GemMetaData
|
5
|
+
@dependencies: Array[Dependency]
|
6
|
+
|
7
|
+
@gem_hash: Hash[String, Dependency]
|
8
|
+
|
9
|
+
def initialize: (Array[Dependency]) -> void
|
10
|
+
|
11
|
+
def fetch: -> Array[Dependency]
|
12
|
+
|
13
|
+
def find: -> Array[Dependency]
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def assign_groups: -> Array[Dependency]
|
18
|
+
|
19
|
+
def find_rubygems_metadata: -> Array[Dependency]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
module Util
|
4
|
+
module BashColor
|
5
|
+
def self.blue: (String?) -> String
|
6
|
+
|
7
|
+
def self.green: (String?) -> String
|
8
|
+
|
9
|
+
def self.magenta: (String?) -> String
|
10
|
+
|
11
|
+
def self.orange: (String?) -> String
|
12
|
+
|
13
|
+
def self.red: (String?) -> String
|
14
|
+
|
15
|
+
def self.yellow: (String?) -> String
|
16
|
+
|
17
|
+
def self.cyan: (String?) -> String
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Package
|
2
|
+
module Audit
|
3
|
+
module Util
|
4
|
+
module SummaryPrinter
|
5
|
+
def self.deprecated: -> void
|
6
|
+
|
7
|
+
def self.outdated: -> void
|
8
|
+
|
9
|
+
def self.report: -> void
|
10
|
+
|
11
|
+
def self.risk: -> void
|
12
|
+
|
13
|
+
def self.total: (Integer) -> void
|
14
|
+
|
15
|
+
def self.vulnerable: -> void
|
16
|
+
|
17
|
+
def risk: -> void
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|