package-audit 0.6.0 → 0.6.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.
- checksums.yaml +4 -4
- data/lib/package/audit/npm/node_collection.rb +17 -0
- data/lib/package/audit/npm/npm_meta_data.rb +24 -2
- data/lib/package/audit/npm/vulnerability_finder.rb +7 -1
- data/lib/package/audit/ruby/bundler_specs.rb +15 -1
- data/lib/package/audit/services/command_parser.rb +28 -6
- data/lib/package/audit/services/config_cleaner.rb +221 -0
- data/lib/package/audit/util/spinner.rb +1 -1
- data/lib/package/audit/util/summary_printer.rb +7 -6
- data/lib/package/audit/version.rb +1 -1
- metadata +9 -53
- data/sig/package/audit/cli.rbs +0 -35
- data/sig/package/audit/const/cmd.rbs +0 -14
- data/sig/package/audit/const/fields.rbs +0 -11
- data/sig/package/audit/const/file.rbs +0 -14
- data/sig/package/audit/const/time.rbs +0 -11
- data/sig/package/audit/const/yaml.rbs +0 -13
- data/sig/package/audit/enum/format.rbs +0 -12
- data/sig/package/audit/enum/group.rbs +0 -15
- data/sig/package/audit/enum/option.rbs +0 -14
- data/sig/package/audit/enum/report.rbs +0 -12
- data/sig/package/audit/enum/risk_explanation.rbs +0 -12
- data/sig/package/audit/enum/risk_type.rbs +0 -12
- data/sig/package/audit/enum/technology.rbs +0 -12
- data/sig/package/audit/enum/vulnerability_type.rbs +0 -15
- data/sig/package/audit/formatter/base.rbs +0 -9
- data/sig/package/audit/formatter/risk_printer.rbs +0 -13
- data/sig/package/audit/formatter/version_date.rbs +0 -13
- data/sig/package/audit/formatter/version_printer.rbs +0 -14
- data/sig/package/audit/formatter/vulnerability.rbs +0 -13
- data/sig/package/audit/models/package.rbs +0 -47
- data/sig/package/audit/models/risk.rbs +0 -12
- data/sig/package/audit/npm/node_collection.rbs +0 -28
- data/sig/package/audit/npm/npm_meta_data.rbs +0 -19
- data/sig/package/audit/npm/vulnerability_finder.rbs +0 -21
- data/sig/package/audit/npm/yarn_lock_parser.rbs +0 -22
- data/sig/package/audit/ruby/bundler_specs.rbs +0 -11
- data/sig/package/audit/ruby/gem_collection.rbs +0 -22
- data/sig/package/audit/ruby/gem_meta_data.rbs +0 -23
- data/sig/package/audit/ruby/vulnerability_finder.rbs +0 -18
- data/sig/package/audit/services/command_parser.rbs +0 -37
- data/sig/package/audit/services/duplicate_package_merger.rbs +0 -11
- data/sig/package/audit/services/package_filter.rbs +0 -26
- data/sig/package/audit/services/package_finder.rbs +0 -26
- data/sig/package/audit/services/package_printer.rbs +0 -30
- data/sig/package/audit/services/risk_calculator.rbs +0 -21
- data/sig/package/audit/technology/detector.rbs +0 -19
- data/sig/package/audit/technology/validator.rbs +0 -19
- data/sig/package/audit/util/bash_color.rbs +0 -21
- data/sig/package/audit/util/risk_legend.rbs +0 -9
- data/sig/package/audit/util/spinner.rbs +0 -24
- data/sig/package/audit/util/summary_printer.rbs +0 -33
- data/sig/package/audit/version.rbs +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4f817af73ba4616da54a934f0f6c4120d921e6026226c20a02c592c6b55e064
|
4
|
+
data.tar.gz: 818282a8f489ba8afd7b9675b78db96c3e2b9b18731415085ff44c25eaf07a77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46267de0e991aaf0e1fdeb4f8c778cc2a59abf49f7d7a0849ae9731c9fb633ae8c958ac1865bf17aa98d50cd359e8e30d16975031f4dfd4fc1a7c2aacf6868fc
|
7
|
+
data.tar.gz: 65c6acb5e9e224f6736781f1db26eee0ec7772f220c87b8e8da1f2cecc872cfb667f78336314ad4b670000f849c60051087cdc9cda1b9e69ad11d896b717c471
|
@@ -58,9 +58,26 @@ module Package
|
|
58
58
|
package_json = JSON.parse(File.read("#{@dir}/#{Const::File::PACKAGE_JSON}"), symbolize_names: true)
|
59
59
|
default_deps = package_json[:dependencies] || {}
|
60
60
|
dev_deps = package_json[:devDependencies] || {}
|
61
|
+
|
62
|
+
# Filter out local dependencies before processing
|
63
|
+
default_deps = filter_local_dependencies(default_deps)
|
64
|
+
dev_deps = filter_local_dependencies(dev_deps)
|
65
|
+
|
61
66
|
[default_deps, dev_deps]
|
62
67
|
end
|
63
68
|
|
69
|
+
def filter_local_dependencies(dependencies)
|
70
|
+
dependencies.reject { |_name, version| local_dependency?(version) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def local_dependency?(version)
|
74
|
+
# Check for local file paths
|
75
|
+
version.to_s.start_with?('file:', 'link:', './', '../') ||
|
76
|
+
version.to_s.include?('file:') ||
|
77
|
+
# Check for git repositories with local paths
|
78
|
+
(version.to_s.start_with?('git+') && version.to_s.include?('file:'))
|
79
|
+
end
|
80
|
+
|
64
81
|
def fetch_from_lock_file
|
65
82
|
default_deps, dev_deps = fetch_from_package_json
|
66
83
|
if File.exist?("#{@dir}/#{Const::File::YARN_LOCK}")
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'net/http'
|
3
|
+
require 'socket'
|
3
4
|
|
4
5
|
module Package
|
5
6
|
module Audit
|
@@ -11,7 +12,7 @@ module Package
|
|
11
12
|
@packages = packages
|
12
13
|
end
|
13
14
|
|
14
|
-
def fetch # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
15
|
+
def fetch # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
15
16
|
threads = @packages.map do |package|
|
16
17
|
Thread.new do
|
17
18
|
response = Net::HTTP.get_response(URI.parse("#{REGISTRY_URL}/#{package.name}"))
|
@@ -20,14 +21,35 @@ module Package
|
|
20
21
|
|
21
22
|
json_package = JSON.parse(response.body, symbolize_names: true)
|
22
23
|
update_meta_data(package, json_package)
|
24
|
+
rescue Net::TimeoutError, Net::OpenTimeout, Net::ReadTimeout => e
|
25
|
+
warn "Warning: Network timeout while fetching metadata for #{package.name}: #{e.message}"
|
26
|
+
Thread.current[:exception] = e
|
27
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e
|
28
|
+
warn "Warning: Network error while fetching metadata for #{package.name}: #{e.message}"
|
29
|
+
Thread.current[:exception] = e
|
23
30
|
rescue StandardError => e
|
24
31
|
Thread.current[:exception] = e
|
25
32
|
end
|
26
33
|
end
|
34
|
+
|
35
|
+
network_errors = []
|
27
36
|
threads.each do |thread|
|
28
37
|
thread.join
|
29
|
-
|
38
|
+
next unless thread[:exception]
|
39
|
+
|
40
|
+
case thread[:exception]
|
41
|
+
when Net::TimeoutError, Net::OpenTimeout, Net::ReadTimeout, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH # rubocop:disable Layout/LineLength
|
42
|
+
network_errors << thread[:exception]
|
43
|
+
else
|
44
|
+
raise thread[:exception]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
unless network_errors.empty?
|
49
|
+
warn "Warning: #{network_errors.size} network error(s) occurred while fetching package metadata."
|
50
|
+
warn 'Some packages may not show complete version information.'
|
30
51
|
end
|
52
|
+
|
31
53
|
@packages
|
32
54
|
end
|
33
55
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
1
3
|
require_relative '../const/cmd'
|
2
4
|
require_relative '../enum/vulnerability_type'
|
3
5
|
|
@@ -14,7 +16,11 @@ module Package
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def run
|
17
|
-
|
19
|
+
# Suppress Node.js url.parse deprecation warnings from yarn audit command
|
20
|
+
command = format(Const::Cmd::YARN_AUDIT_JSON, @dir)
|
21
|
+
env_vars = { 'NODE_NO_WARNINGS' => '1' }
|
22
|
+
|
23
|
+
json_string_lines, = Open3.capture3(env_vars, command)
|
18
24
|
array = json_string_lines.scan(AUDIT_ADVISORY_REGEX)
|
19
25
|
|
20
26
|
vulnerability_json_array = JSON.parse("[#{array.join(',')}]", symbolize_names: true)
|
@@ -9,10 +9,11 @@ module Package
|
|
9
9
|
module Ruby
|
10
10
|
class BundlerSpecs
|
11
11
|
def self.all(dir)
|
12
|
-
Bundler.with_unbundled_env do
|
12
|
+
specs = Bundler.with_unbundled_env do
|
13
13
|
ENV['BUNDLE_GEMFILE'] = "#{dir}/Gemfile"
|
14
14
|
Bundler.ui.silence { Bundler.definition.resolve }
|
15
15
|
end
|
16
|
+
filter_local_dependencies(specs)
|
16
17
|
end
|
17
18
|
|
18
19
|
def self.gemfile(dir)
|
@@ -30,6 +31,19 @@ module Package
|
|
30
31
|
end
|
31
32
|
gemfile_specs
|
32
33
|
end
|
34
|
+
|
35
|
+
def self.filter_local_dependencies(specs)
|
36
|
+
specs.reject { |spec| local_dependency?(spec) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.local_dependency?(spec)
|
40
|
+
# Check if the gem has a local source (path or git with local path)
|
41
|
+
source = spec.source
|
42
|
+
return true if source.is_a?(Bundler::Source::Path)
|
43
|
+
return true if source.is_a?(Bundler::Source::Git) && source.uri.start_with?('file:', './', '../')
|
44
|
+
|
45
|
+
false
|
46
|
+
end
|
33
47
|
end
|
34
48
|
end
|
35
49
|
end
|
@@ -6,6 +6,7 @@ require_relative '../technology/detector'
|
|
6
6
|
require_relative '../technology/validator'
|
7
7
|
require_relative '../util/spinner'
|
8
8
|
require_relative '../util/summary_printer'
|
9
|
+
require_relative 'config_cleaner'
|
9
10
|
require_relative 'package_finder'
|
10
11
|
require_relative 'package_printer'
|
11
12
|
|
@@ -13,7 +14,7 @@ require 'yaml'
|
|
13
14
|
|
14
15
|
module Package
|
15
16
|
module Audit
|
16
|
-
class CommandParser
|
17
|
+
class CommandParser # rubocop:disable Metrics/ClassLength
|
17
18
|
def initialize(dir, options, report)
|
18
19
|
@dir = dir
|
19
20
|
@options = options
|
@@ -22,7 +23,7 @@ module Package
|
|
22
23
|
@groups = @options[Enum::Option::GROUP]
|
23
24
|
@technologies = parse_technologies!
|
24
25
|
validate_format!
|
25
|
-
@spinner = Util::Spinner.new(
|
26
|
+
@spinner = Util::Spinner.new("Evaluating packages and their dependencies for #{human_readable_technologies}...")
|
26
27
|
end
|
27
28
|
|
28
29
|
def run
|
@@ -42,6 +43,7 @@ module Package
|
|
42
43
|
def process_technologies # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
43
44
|
mutex = Mutex.new
|
44
45
|
cumulative_pkgs = []
|
46
|
+
all_packages_for_config = []
|
45
47
|
thread_index = 0
|
46
48
|
|
47
49
|
@spinner.start
|
@@ -49,11 +51,14 @@ module Package
|
|
49
51
|
Thread.new do
|
50
52
|
all_pkgs, ignored_pkgs = PackageFinder.new(@config, @dir, @report, @groups).run(technology)
|
51
53
|
ignored_pkgs = [] if @options[Enum::Option::INCLUDE_IGNORED]
|
52
|
-
|
54
|
+
active_pkgs = (all_pkgs || []) - (ignored_pkgs || [])
|
55
|
+
cumulative_pkgs += active_pkgs
|
56
|
+
mutex.synchronize { all_packages_for_config += all_pkgs || [] }
|
57
|
+
|
53
58
|
sleep 0.1 while technology_index != thread_index # print each technology in order
|
54
59
|
mutex.synchronize do
|
55
60
|
@spinner.stop
|
56
|
-
print_results(technology,
|
61
|
+
print_results(technology, active_pkgs, ignored_pkgs || [])
|
57
62
|
thread_index += 1
|
58
63
|
@spinner.start
|
59
64
|
end
|
@@ -65,6 +70,10 @@ module Package
|
|
65
70
|
thread.join
|
66
71
|
raise thread[:exception] if thread[:exception]
|
67
72
|
end
|
73
|
+
|
74
|
+
@spinner.stop # Stop spinner before cleaning config to ensure clean output
|
75
|
+
clean_config(all_packages_for_config)
|
76
|
+
|
68
77
|
cumulative_pkgs.any? ? 1 : 0
|
69
78
|
ensure
|
70
79
|
@spinner.stop
|
@@ -73,7 +82,7 @@ module Package
|
|
73
82
|
def print_results(technology, pkgs, ignored_pkgs)
|
74
83
|
PackagePrinter.new(@options, pkgs).print(Const::Fields::DEFAULT)
|
75
84
|
print_summary(technology, pkgs, ignored_pkgs) unless @options[Enum::Option::FORMAT] == Enum::Format::CSV
|
76
|
-
print_disclaimer(technology) unless @options[Enum::Option::FORMAT]
|
85
|
+
print_disclaimer(technology) unless @options[Enum::Option::FORMAT] || pkgs.empty?
|
77
86
|
end
|
78
87
|
|
79
88
|
def print_summary(technology, pkgs, ignored_pkgs)
|
@@ -123,7 +132,20 @@ module Package
|
|
123
132
|
def parse_technologies!
|
124
133
|
technology_validator = Technology::Validator.new(@dir)
|
125
134
|
@options[Enum::Option::TECHNOLOGY]&.each { |technology| technology_validator.validate! technology }
|
126
|
-
@options[Enum::Option::TECHNOLOGY] || Technology::Detector.new(@dir).detect
|
135
|
+
(@options[Enum::Option::TECHNOLOGY] || Technology::Detector.new(@dir).detect).sort
|
136
|
+
end
|
137
|
+
|
138
|
+
def clean_config(all_packages)
|
139
|
+
ConfigCleaner.new(@dir, @config, all_packages, @options).run
|
140
|
+
end
|
141
|
+
|
142
|
+
def human_readable_technologies
|
143
|
+
array = @technologies.map(&:capitalize)
|
144
|
+
return '' if array.nil?
|
145
|
+
return array.join if array.size <= 1
|
146
|
+
return array.join(' and ') if array.size == 2
|
147
|
+
|
148
|
+
"#{array[0..-2].join(', ')}, and #{array.last}"
|
127
149
|
end
|
128
150
|
end
|
129
151
|
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require_relative '../const/file'
|
2
|
+
require_relative '../const/yaml'
|
3
|
+
require_relative '../enum/option'
|
4
|
+
require_relative '../enum/technology'
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module Package
|
10
|
+
module Audit
|
11
|
+
class ConfigCleaner # rubocop:disable Metrics/ClassLength
|
12
|
+
def initialize(dir, config, all_packages, options)
|
13
|
+
@dir = dir
|
14
|
+
@config = config
|
15
|
+
@all_packages = all_packages
|
16
|
+
@options = options
|
17
|
+
@config_file_path = determine_config_file_path
|
18
|
+
@removed_packages = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return unless @config && File.exist?(@config_file_path)
|
23
|
+
|
24
|
+
cleaned_config = clean_config
|
25
|
+
|
26
|
+
return unless config_changed?(cleaned_config)
|
27
|
+
|
28
|
+
write_config_file(cleaned_config)
|
29
|
+
print_summary unless @options[Enum::Option::FORMAT]
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :removed_packages
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def determine_config_file_path
|
37
|
+
if @options[Enum::Option::CONFIG].nil?
|
38
|
+
"#{@dir}/#{Const::File::CONFIG}"
|
39
|
+
else
|
40
|
+
@options[Enum::Option::CONFIG]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def clean_config
|
45
|
+
return {} unless @config
|
46
|
+
|
47
|
+
cleaned = {}
|
48
|
+
|
49
|
+
if @config[Const::YAML::TECHNOLOGY]
|
50
|
+
cleaned[Const::YAML::TECHNOLOGY] = {}
|
51
|
+
|
52
|
+
# Sort technologies alphabetically to ensure consistent ordering
|
53
|
+
@config[Const::YAML::TECHNOLOGY].sort.each do |technology, packages|
|
54
|
+
cleaned_packages = clean_packages_for_technology(technology, packages)
|
55
|
+
cleaned[Const::YAML::TECHNOLOGY][technology] = cleaned_packages unless cleaned_packages.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
# Remove the technology key if no technologies have any packages
|
59
|
+
cleaned.delete(Const::YAML::TECHNOLOGY) if cleaned[Const::YAML::TECHNOLOGY].empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
cleaned
|
63
|
+
end
|
64
|
+
|
65
|
+
def clean_packages_for_technology(technology, packages)
|
66
|
+
return {} unless packages.is_a?(Hash)
|
67
|
+
|
68
|
+
current_packages = current_packages_for_technology(technology)
|
69
|
+
cleaned_packages = {}
|
70
|
+
|
71
|
+
packages.each do |package_name, package_config|
|
72
|
+
next unless package_config.is_a?(Hash)
|
73
|
+
|
74
|
+
if should_keep_package?(package_name, package_config, current_packages)
|
75
|
+
cleaned_packages[package_name] = sort_package_config(package_config)
|
76
|
+
else
|
77
|
+
track_removed_package(technology, package_name, package_config)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sort package names alphabetically
|
82
|
+
cleaned_packages.sort.to_h
|
83
|
+
end
|
84
|
+
|
85
|
+
def current_packages_for_technology(technology)
|
86
|
+
@all_packages.select { |pkg| pkg.technology == technology }
|
87
|
+
.to_h { |pkg| [pkg.name, pkg.version] }
|
88
|
+
end
|
89
|
+
|
90
|
+
def should_keep_package?(package_name, package_config, current_packages)
|
91
|
+
config_version = package_config[Const::YAML::VERSION]
|
92
|
+
current_version = current_packages[package_name]
|
93
|
+
|
94
|
+
# Keep the package if it exists and the version matches
|
95
|
+
current_version && config_version == current_version
|
96
|
+
end
|
97
|
+
|
98
|
+
def sort_package_config(package_config)
|
99
|
+
sorted_config = {}
|
100
|
+
|
101
|
+
# Add version first if it exists
|
102
|
+
if package_config[Const::YAML::VERSION]
|
103
|
+
sorted_config[Const::YAML::VERSION] =
|
104
|
+
package_config[Const::YAML::VERSION]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Add other keys in alphabetical order
|
108
|
+
other_keys = (package_config.keys - [Const::YAML::VERSION]).sort
|
109
|
+
other_keys.each do |key|
|
110
|
+
sorted_config[key] = package_config[key]
|
111
|
+
end
|
112
|
+
|
113
|
+
sorted_config
|
114
|
+
end
|
115
|
+
|
116
|
+
def track_removed_package(technology, package_name, package_config)
|
117
|
+
@removed_packages << {
|
118
|
+
technology: technology,
|
119
|
+
name: package_name,
|
120
|
+
version: package_config[Const::YAML::VERSION],
|
121
|
+
reason: determine_removal_reason(package_name, package_config)
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
def determine_removal_reason(package_name, package_config)
|
126
|
+
technology = find_technology_for_package(package_name)
|
127
|
+
return 'unknown reason' unless technology
|
128
|
+
|
129
|
+
current_packages = current_packages_for_technology(technology)
|
130
|
+
config_version = package_config[Const::YAML::VERSION]
|
131
|
+
current_version = current_packages[package_name]
|
132
|
+
|
133
|
+
determine_reason_based_on_versions(package_name, technology, config_version, current_version)
|
134
|
+
end
|
135
|
+
|
136
|
+
def find_technology_for_package(package_name)
|
137
|
+
@config[Const::YAML::TECHNOLOGY].each do |tech, packages|
|
138
|
+
return tech if packages.key?(package_name)
|
139
|
+
end
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
|
143
|
+
def determine_reason_based_on_versions(package_name, technology, config_version, current_version)
|
144
|
+
if current_version.nil?
|
145
|
+
determine_reason_for_missing_package(package_name, technology)
|
146
|
+
elsif config_version != current_version
|
147
|
+
"version changed from #{config_version} to #{current_version}"
|
148
|
+
else
|
149
|
+
'unknown reason'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def determine_reason_for_missing_package(package_name, technology)
|
154
|
+
if package_exists_in_project_files?(package_name, technology)
|
155
|
+
'package version has changed'
|
156
|
+
else
|
157
|
+
'package no longer exists'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def package_exists_in_project_files?(package_name, technology)
|
162
|
+
case technology
|
163
|
+
when Enum::Technology::RUBY
|
164
|
+
package_exists_in_gemfile?(package_name)
|
165
|
+
when Enum::Technology::NODE
|
166
|
+
package_exists_in_package_json?(package_name)
|
167
|
+
else
|
168
|
+
false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def package_exists_in_gemfile?(package_name)
|
173
|
+
gemfile_path = "#{@dir}/#{Const::File::GEMFILE}"
|
174
|
+
return false unless File.exist?(gemfile_path)
|
175
|
+
|
176
|
+
gemfile_content = File.read(gemfile_path)
|
177
|
+
# Check for gem declarations with single or double quotes
|
178
|
+
gemfile_content.match?(/^\s*gem\s+['"]#{Regexp.escape(package_name)}['"]/)
|
179
|
+
end
|
180
|
+
|
181
|
+
def package_exists_in_package_json?(package_name)
|
182
|
+
package_json_path = "#{@dir}/#{Const::File::PACKAGE_JSON}"
|
183
|
+
return false unless File.exist?(package_json_path)
|
184
|
+
|
185
|
+
begin
|
186
|
+
package_json = JSON.parse(File.read(package_json_path))
|
187
|
+
dependencies = package_json['dependencies'] || {}
|
188
|
+
dev_dependencies = package_json['devDependencies'] || {}
|
189
|
+
|
190
|
+
dependencies.key?(package_name) || dev_dependencies.key?(package_name)
|
191
|
+
rescue JSON::ParserError
|
192
|
+
false
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def config_changed?(cleaned_config)
|
197
|
+
# Compare YAML representations to detect key reordering
|
198
|
+
cleaned_config.to_yaml != @config.to_yaml
|
199
|
+
end
|
200
|
+
|
201
|
+
def write_config_file(cleaned_config)
|
202
|
+
File.write(@config_file_path, cleaned_config.to_yaml)
|
203
|
+
end
|
204
|
+
|
205
|
+
def print_summary
|
206
|
+
return if @removed_packages.empty?
|
207
|
+
|
208
|
+
puts
|
209
|
+
puts "Cleaned up #{@removed_packages.count} package(s) from #{File.basename(@config_file_path)}:"
|
210
|
+
|
211
|
+
# Sort by technology then by name for consistent output
|
212
|
+
@removed_packages.sort_by { |pkg| [pkg[:technology], pkg[:name]] }.each do |removed_package|
|
213
|
+
package_info = "#{removed_package[:name]}@#{removed_package[:version]}"
|
214
|
+
tech_info = "(#{removed_package[:technology]})"
|
215
|
+
reason = removed_package[:reason]
|
216
|
+
puts " - #{package_info} #{tech_info}: #{reason}"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -19,7 +19,7 @@ module Package
|
|
19
19
|
@thread = Thread.new do
|
20
20
|
step = 0
|
21
21
|
while @running
|
22
|
-
if @running &&
|
22
|
+
if @running && ENV['RUBY_ENV'] != 'test' && ENV['RACK_ENV'] != 'test'
|
23
23
|
print "\r#{@message} #{STATES[step % STATES.length]}"
|
24
24
|
end
|
25
25
|
sleep ANIMATION_SPEED
|
@@ -18,18 +18,18 @@ module Package
|
|
18
18
|
|
19
19
|
def self.vulnerable(technology, cmd)
|
20
20
|
printf("%<info>s\n%<cmd>s\n\n",
|
21
|
-
info: Util::BashColor.blue("
|
21
|
+
info: Util::BashColor.blue("For more information about #{technology.capitalize} vulnerabilities run:"),
|
22
22
|
cmd: Util::BashColor.magenta(" > #{cmd}"))
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.total(technology, report, pkgs, ignored_pkgs)
|
26
26
|
if ignored_pkgs.any?
|
27
|
-
puts Util::BashColor.cyan("Found a total of #{pkgs.length} #{technology} packages " \
|
27
|
+
puts Util::BashColor.cyan("Found a total of #{pkgs.length} #{technology.capitalize} packages " \
|
28
28
|
"(#{ignored_pkgs.length} ignored).\n")
|
29
29
|
elsif pkgs.any?
|
30
|
-
puts Util::BashColor.cyan("Found a total of #{pkgs.length} #{technology} packages.\n")
|
30
|
+
puts Util::BashColor.cyan("Found a total of #{pkgs.length} #{technology.capitalize} packages.\n")
|
31
31
|
else
|
32
|
-
puts Util::BashColor.green("There are no #{report} #{technology} packages!\n")
|
32
|
+
puts Util::BashColor.green("There are no #{report} #{technology.capitalize} packages!\n")
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -66,10 +66,11 @@ module Package
|
|
66
66
|
print status_message(stats)
|
67
67
|
print Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN
|
68
68
|
puts
|
69
|
-
puts Util::BashColor.green("There are no deprecated, outdated or vulnerable #{technology} " \
|
69
|
+
puts Util::BashColor.green("There are no deprecated, outdated or vulnerable #{technology.capitalize} " \
|
70
70
|
"packages (#{ignored_pkgs.length} ignored)!\n")
|
71
71
|
else
|
72
|
-
puts Util::BashColor.green("There are no deprecated, outdated or vulnerable #{technology}
|
72
|
+
puts Util::BashColor.green("There are no deprecated, outdated or vulnerable #{technology.capitalize} " \
|
73
|
+
"packages!\n")
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: package-audit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- Vadim Kononov
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: bundler-audit
|
@@ -41,7 +40,7 @@ dependencies:
|
|
41
40
|
description: A useful tool for patch management and prioritization, package-audit
|
42
41
|
produces a list of dependencies that are outdated, deprecated or have security vulnerabilities.
|
43
42
|
email:
|
44
|
-
-
|
43
|
+
- vadim@konoson.com
|
45
44
|
executables:
|
46
45
|
- package-audit
|
47
46
|
extensions: []
|
@@ -78,6 +77,7 @@ files:
|
|
78
77
|
- lib/package/audit/ruby/gem_meta_data.rb
|
79
78
|
- lib/package/audit/ruby/vulnerability_finder.rb
|
80
79
|
- lib/package/audit/services/command_parser.rb
|
80
|
+
- lib/package/audit/services/config_cleaner.rb
|
81
81
|
- lib/package/audit/services/duplicate_package_merger.rb
|
82
82
|
- lib/package/audit/services/package_filter.rb
|
83
83
|
- lib/package/audit/services/package_finder.rb
|
@@ -90,56 +90,13 @@ files:
|
|
90
90
|
- lib/package/audit/util/spinner.rb
|
91
91
|
- lib/package/audit/util/summary_printer.rb
|
92
92
|
- lib/package/audit/version.rb
|
93
|
-
|
94
|
-
- sig/package/audit/const/cmd.rbs
|
95
|
-
- sig/package/audit/const/fields.rbs
|
96
|
-
- sig/package/audit/const/file.rbs
|
97
|
-
- sig/package/audit/const/time.rbs
|
98
|
-
- sig/package/audit/const/yaml.rbs
|
99
|
-
- sig/package/audit/enum/format.rbs
|
100
|
-
- sig/package/audit/enum/group.rbs
|
101
|
-
- sig/package/audit/enum/option.rbs
|
102
|
-
- sig/package/audit/enum/report.rbs
|
103
|
-
- sig/package/audit/enum/risk_explanation.rbs
|
104
|
-
- sig/package/audit/enum/risk_type.rbs
|
105
|
-
- sig/package/audit/enum/technology.rbs
|
106
|
-
- sig/package/audit/enum/vulnerability_type.rbs
|
107
|
-
- sig/package/audit/formatter/base.rbs
|
108
|
-
- sig/package/audit/formatter/risk_printer.rbs
|
109
|
-
- sig/package/audit/formatter/version_date.rbs
|
110
|
-
- sig/package/audit/formatter/version_printer.rbs
|
111
|
-
- sig/package/audit/formatter/vulnerability.rbs
|
112
|
-
- sig/package/audit/models/package.rbs
|
113
|
-
- sig/package/audit/models/risk.rbs
|
114
|
-
- sig/package/audit/npm/node_collection.rbs
|
115
|
-
- sig/package/audit/npm/npm_meta_data.rbs
|
116
|
-
- sig/package/audit/npm/vulnerability_finder.rbs
|
117
|
-
- sig/package/audit/npm/yarn_lock_parser.rbs
|
118
|
-
- sig/package/audit/ruby/bundler_specs.rbs
|
119
|
-
- sig/package/audit/ruby/gem_collection.rbs
|
120
|
-
- sig/package/audit/ruby/gem_meta_data.rbs
|
121
|
-
- sig/package/audit/ruby/vulnerability_finder.rbs
|
122
|
-
- sig/package/audit/services/command_parser.rbs
|
123
|
-
- sig/package/audit/services/duplicate_package_merger.rbs
|
124
|
-
- sig/package/audit/services/package_filter.rbs
|
125
|
-
- sig/package/audit/services/package_finder.rbs
|
126
|
-
- sig/package/audit/services/package_printer.rbs
|
127
|
-
- sig/package/audit/services/risk_calculator.rbs
|
128
|
-
- sig/package/audit/technology/detector.rbs
|
129
|
-
- sig/package/audit/technology/validator.rbs
|
130
|
-
- sig/package/audit/util/bash_color.rbs
|
131
|
-
- sig/package/audit/util/risk_legend.rbs
|
132
|
-
- sig/package/audit/util/spinner.rbs
|
133
|
-
- sig/package/audit/util/summary_printer.rbs
|
134
|
-
- sig/package/audit/version.rbs
|
135
|
-
homepage: https://github.com/tactica/package-audit
|
93
|
+
homepage: https://github.com/vkononov/package-audit
|
136
94
|
licenses:
|
137
95
|
- MIT
|
138
96
|
metadata:
|
139
|
-
homepage_uri: https://github.com/
|
140
|
-
source_code_uri: https://github.com/
|
97
|
+
homepage_uri: https://github.com/vkononov/package-audit
|
98
|
+
source_code_uri: https://github.com/vkononov/package-audit
|
141
99
|
rubygems_mfa_required: 'true'
|
142
|
-
post_install_message:
|
143
100
|
rdoc_options: []
|
144
101
|
require_paths:
|
145
102
|
- lib
|
@@ -154,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
111
|
- !ruby/object:Gem::Version
|
155
112
|
version: '0'
|
156
113
|
requirements: []
|
157
|
-
rubygems_version: 3.
|
158
|
-
signing_key:
|
114
|
+
rubygems_version: 3.6.7
|
159
115
|
specification_version: 4
|
160
116
|
summary: A helper tool to find outdated, deprecated and vulnerable dependencies.
|
161
117
|
test_files: []
|
data/sig/package/audit/cli.rbs
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
module Package
|
2
|
-
module Audit
|
3
|
-
class CLI
|
4
|
-
def self.exit_on_failure?: -> bool
|
5
|
-
|
6
|
-
def default: (String) -> void
|
7
|
-
|
8
|
-
def deprecated: (String) -> void
|
9
|
-
|
10
|
-
def outdated: (String) -> void
|
11
|
-
|
12
|
-
def report: (String) -> void
|
13
|
-
|
14
|
-
def respond_to_missing?: -> bool
|
15
|
-
|
16
|
-
def risk: -> void
|
17
|
-
|
18
|
-
def version: -> void
|
19
|
-
|
20
|
-
def vulnerable: (String) -> void
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def exit_with_error: (String) -> void
|
25
|
-
|
26
|
-
def exit_with_success: (String) -> void
|
27
|
-
|
28
|
-
def print_total: (Integer) -> void
|
29
|
-
|
30
|
-
def print_vulnerability_info: (String) -> void
|
31
|
-
|
32
|
-
def within_rescue_block: (String) { () -> void } -> void
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|