package-audit 0.8.2 → 0.9.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/README.md +14 -17
- data/lib/package/audit/npm/node_collection.rb +17 -11
- data/lib/package/audit/npm/package_lock_parser.rb +80 -0
- data/lib/package/audit/npm/semver_range.rb +138 -0
- data/lib/package/audit/npm/vulnerability_finder.rb +12 -12
- data/lib/package/audit/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a9f8b791f93ef77f2eecb40a1d0bacaebf40c94a06e938ef7528f26281ac30b
|
|
4
|
+
data.tar.gz: e9a24f0114518ba97deaa1687ca5c786fee0ba54dd868cbaec48c4d6d183be7b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e106be4632777be3433e572bb60b34a9b8fa4f7e038f4be86b1d9744ffcaf2e709804bcd4ffffd2b001ee82887ea70cbb7234d2df37a4033224f155ac84b4b88
|
|
7
|
+
data.tar.gz: 400eb639d4f16d4671bc10b1e0b76ad7e49f7f3991239c8f3c5a7dbee5138fcc46d3ae819bcb06021d45a3d46e4ca2cff8e755760dda387976b2fd67d8c8aa03
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://github.com/vkononov/package-audit/blob/main/.github/workflows/test.yml)
|
|
5
5
|
[](https://github.com/vkononov/package-audit/actions/workflows/test.yml)
|
|
6
6
|
[](https://github.com/vkononov/package-audit/actions/workflows/lint.yml)
|
|
7
|
-
[](https://opensource.org/
|
|
7
|
+
[](https://opensource.org/license/MIT)
|
|
8
8
|
|
|
9
9
|
A useful tool for patch management and prioritization, `package-audit` produces a list of dependencies that are outdated, deprecated or have security vulnerabilities.
|
|
10
10
|
|
|
@@ -15,7 +15,7 @@ Supports **Ruby 2.6 → latest**, with the full Ruby matrix tested daily in CI.
|
|
|
15
15
|
## Supported Technologies
|
|
16
16
|
|
|
17
17
|
* Ruby
|
|
18
|
-
* Node (using Yarn)
|
|
18
|
+
* Node (using npm or Yarn)
|
|
19
19
|
|
|
20
20
|
## Report Example
|
|
21
21
|
|
|
@@ -75,6 +75,9 @@ Show only specific risk types:
|
|
|
75
75
|
# Show only deprecated packages
|
|
76
76
|
package-audit [DIR] --deprecated
|
|
77
77
|
|
|
78
|
+
# Show only outdated packages
|
|
79
|
+
package-audit [DIR] --outdated
|
|
80
|
+
|
|
78
81
|
# Show only vulnerable packages
|
|
79
82
|
package-audit [DIR] --vulnerable
|
|
80
83
|
|
|
@@ -93,24 +96,15 @@ package-audit [DIR] --skip-deprecated
|
|
|
93
96
|
# Show everything except outdated packages
|
|
94
97
|
package-audit [DIR] --skip-outdated
|
|
95
98
|
|
|
99
|
+
# Show everything except vulnerable packages
|
|
100
|
+
package-audit [DIR] --skip-vulnerable
|
|
101
|
+
|
|
96
102
|
# Show only vulnerable packages (exclude deprecated and outdated)
|
|
97
103
|
package-audit [DIR] --skip-deprecated --skip-outdated
|
|
98
104
|
```
|
|
99
105
|
|
|
100
106
|
**Important:** Packages with multiple risk types are handled intelligently. For example, a package that is both outdated and vulnerable will still appear when using `--skip-outdated` because it has a vulnerability.
|
|
101
107
|
|
|
102
|
-
### Understanding the Flags Column
|
|
103
|
-
|
|
104
|
-
The Flags column in the output shows which risk types apply to each package:
|
|
105
|
-
|
|
106
|
-
- `⦗V··⦘` - Vulnerable (has security vulnerabilities)
|
|
107
|
-
- `⦗·O·⦘` - Outdated (newer version available)
|
|
108
|
-
- `⦗··D⦘` - Deprecated (no updates in 2+ years)
|
|
109
|
-
- `⦗VO·⦘` - Both vulnerable and outdated
|
|
110
|
-
- `⦗VOD⦘` - All three risk types apply
|
|
111
|
-
|
|
112
|
-
The footer uses the same notation `⦗V⦘ulnerable, ⦗O⦘utdated, ⦗D⦘eprecated` as a legend.
|
|
113
|
-
|
|
114
108
|
### Scoping Options
|
|
115
109
|
|
|
116
110
|
Filter by technology or group:
|
|
@@ -144,11 +138,14 @@ package-audit [DIR] --include-ignored
|
|
|
144
138
|
### Output Formats
|
|
145
139
|
|
|
146
140
|
```bash
|
|
147
|
-
# CSV format
|
|
141
|
+
# CSV format (-f is an alias for --format)
|
|
148
142
|
package-audit [DIR] --format csv
|
|
149
143
|
|
|
150
144
|
# Markdown format
|
|
151
145
|
package-audit [DIR] --format md
|
|
146
|
+
|
|
147
|
+
# CSV without the header row
|
|
148
|
+
package-audit [DIR] --format csv --exclude-headers
|
|
152
149
|
```
|
|
153
150
|
|
|
154
151
|
### Other Commands
|
|
@@ -157,7 +154,7 @@ package-audit [DIR] --format md
|
|
|
157
154
|
# Show how risk is calculated
|
|
158
155
|
package-audit risk
|
|
159
156
|
|
|
160
|
-
# Show version
|
|
157
|
+
# Show version (-v and --version also work)
|
|
161
158
|
package-audit version
|
|
162
159
|
```
|
|
163
160
|
|
|
@@ -215,4 +212,4 @@ technology:
|
|
|
215
212
|
|
|
216
213
|
## License
|
|
217
214
|
|
|
218
|
-
The gem is available as open source under the terms of the [MIT License](
|
|
215
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/license/MIT).
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require_relative '../const/file'
|
|
2
2
|
require_relative '../services/duplicate_package_merger'
|
|
3
3
|
require_relative 'npm_meta_data'
|
|
4
|
+
require_relative 'package_lock_parser'
|
|
4
5
|
require_relative 'vulnerability_finder'
|
|
5
6
|
require_relative 'yarn_lock_parser'
|
|
6
7
|
|
|
@@ -73,22 +74,27 @@ module Package
|
|
|
73
74
|
dependencies.reject { |_name, version| local_dependency?(version) }
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
# Packages that live in the project itself (paths, workspaces, portals)
|
|
78
|
+
# are not published to the registry and cannot be audited against it.
|
|
76
79
|
def local_dependency?(version)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
version.to_s.include?('file:') ||
|
|
80
|
-
# Check for git repositories with local paths
|
|
81
|
-
(version.to_s.start_with?('git+') && version.to_s.include?('file:'))
|
|
80
|
+
version.to_s.start_with?('file:', 'link:', 'workspace:', 'portal:', './', '../') ||
|
|
81
|
+
version.to_s.include?('file:')
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
def fetch_from_lock_file
|
|
85
85
|
default_deps, dev_deps, resolutions = fetch_from_package_json
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
parser = lock_file_parser
|
|
87
|
+
return [] if parser.nil?
|
|
88
|
+
|
|
89
|
+
parser.fetch(default_deps || {}, dev_deps || {}, resolutions || {})
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def lock_file_parser
|
|
93
|
+
yarn_lock = "#{@dir}/#{Const::File::YARN_LOCK}"
|
|
94
|
+
return YarnLockParser.new(yarn_lock) if File.exist?(yarn_lock)
|
|
95
|
+
|
|
96
|
+
package_lock = "#{@dir}/#{Const::File::PACKAGE_LOCK_JSON}"
|
|
97
|
+
PackageLockParser.new(package_lock) if File.exist?(package_lock)
|
|
92
98
|
end
|
|
93
99
|
end
|
|
94
100
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
require_relative '../enum/group'
|
|
4
|
+
require_relative '../models/package'
|
|
5
|
+
|
|
6
|
+
# Ruby 2.7 added this. A missing lockfile entry raises it, same as the yarn parser.
|
|
7
|
+
Object.const_set(:NoMatchingPatternError, Class.new(StandardError)) unless defined?(NoMatchingPatternError)
|
|
8
|
+
|
|
9
|
+
module Package
|
|
10
|
+
module Audit
|
|
11
|
+
module Npm
|
|
12
|
+
# Reads package-lock.json (lockfileVersion 1, 2 and 3) and resolves the
|
|
13
|
+
# installed version of each direct dependency.
|
|
14
|
+
class PackageLockParser
|
|
15
|
+
NODE_MODULES = 'node_modules/'
|
|
16
|
+
|
|
17
|
+
def initialize(package_lock_path)
|
|
18
|
+
@package_lock_path = package_lock_path
|
|
19
|
+
@lock = JSON.parse(File.read(package_lock_path))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def fetch(default_deps, dev_deps, _resolutions = {})
|
|
23
|
+
default_deps.merge(dev_deps).map do |dep_name, _expected_version|
|
|
24
|
+
version = direct_version(dep_name)
|
|
25
|
+
raise NoMatchingPatternError, "Unable to find \"#{dep_name}\" in #{@package_lock_path}" if version.nil?
|
|
26
|
+
|
|
27
|
+
pkg = Package.new(dep_name.to_s, version, 'node')
|
|
28
|
+
pkg.update groups: package_groups(dep_name, dev_deps)
|
|
29
|
+
pkg
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Every installed package, including transitive ones, as [name, version] pairs.
|
|
34
|
+
# Entries outside node_modules are workspace sources, not registry packages.
|
|
35
|
+
def all_packages
|
|
36
|
+
if @lock.key?('packages')
|
|
37
|
+
@lock['packages'].map do |path, entry|
|
|
38
|
+
next if entry['version'].nil? || !path.include?(NODE_MODULES)
|
|
39
|
+
|
|
40
|
+
[name_from_path(path), entry['version']]
|
|
41
|
+
end.compact
|
|
42
|
+
else
|
|
43
|
+
walk_v1(@lock['dependencies'] || {})
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def direct_version(dep_name)
|
|
50
|
+
if @lock.key?('packages')
|
|
51
|
+
@lock.dig('packages', "#{NODE_MODULES}#{dep_name}", 'version')
|
|
52
|
+
else
|
|
53
|
+
@lock.dig('dependencies', dep_name, 'version')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def package_groups(dep_name, dev_deps)
|
|
58
|
+
if dev_deps.key?(dep_name)
|
|
59
|
+
[Enum::Group::DEV]
|
|
60
|
+
else
|
|
61
|
+
[Enum::Group::DEFAULT, Enum::Group::DEV]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# "node_modules/a/node_modules/@scope/b" => "@scope/b"
|
|
66
|
+
def name_from_path(path)
|
|
67
|
+
path.split(NODE_MODULES).last
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def walk_v1(dependencies, packages = [])
|
|
71
|
+
dependencies.each do |name, entry|
|
|
72
|
+
packages << [name, entry['version']] unless entry['version'].nil?
|
|
73
|
+
walk_v1(entry['dependencies'] || {}, packages)
|
|
74
|
+
end
|
|
75
|
+
packages
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
module Package
|
|
2
|
+
module Audit
|
|
3
|
+
module Npm
|
|
4
|
+
# Evaluates npm semver ranges (as used in registry advisories) against an
|
|
5
|
+
# installed version. Supports comparator sets joined by "||", hyphen
|
|
6
|
+
# ranges, caret and tilde ranges, and x-ranges. A range that cannot be
|
|
7
|
+
# understood is treated as matching, so an advisory is never silently
|
|
8
|
+
# dropped because of unusual syntax.
|
|
9
|
+
class SemverRange
|
|
10
|
+
WILDCARDS = %w[* x X].freeze
|
|
11
|
+
|
|
12
|
+
def initialize(range)
|
|
13
|
+
@range = range.to_s.strip
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def satisfied_by?(version)
|
|
17
|
+
return true if @range.empty?
|
|
18
|
+
|
|
19
|
+
installed = parse_version(version)
|
|
20
|
+
return false if installed.nil?
|
|
21
|
+
|
|
22
|
+
@range.split('||').any? { |set| comparator_set_matches?(set.strip, installed) }
|
|
23
|
+
rescue ArgumentError
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def comparator_set_matches?(set, installed)
|
|
30
|
+
return true if set.empty? || WILDCARDS.include?(set)
|
|
31
|
+
|
|
32
|
+
Gem::Requirement.new(*requirements_for(set)).satisfied_by?(installed)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def requirements_for(set)
|
|
36
|
+
return hyphen_requirements(*set.split(/\s+-\s+/, 2)) if set.match?(/\s-\s/)
|
|
37
|
+
|
|
38
|
+
set.split(/\s+/).flat_map { |comparator| comparator_requirements(comparator) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def comparator_requirements(comparator)
|
|
42
|
+
case comparator
|
|
43
|
+
when /\A\^(.+)\z/ then caret_requirements(Regexp.last_match(1))
|
|
44
|
+
when /\A~>?(.+)\z/ then tilde_requirements(Regexp.last_match(1))
|
|
45
|
+
when /\A(>=|<=|>|<|=)\s*(.+)\z/ then operator_requirements(Regexp.last_match(1), Regexp.last_match(2))
|
|
46
|
+
else x_range_requirements(comparator)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def operator_requirements(operator, partial)
|
|
51
|
+
parts = version_parts(partial)
|
|
52
|
+
return [] if parts.empty?
|
|
53
|
+
return ["#{operator} #{normalize(partial)}"] if parts.length == 3 || partial.include?('-')
|
|
54
|
+
|
|
55
|
+
partial_operator_requirements(operator, fill(parts), bump(parts))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# ">=1.2" means >=1.2.0, ">1.2" means >=1.3.0, "<=1.2" means <1.3.0, "=1.2" means 1.2.x
|
|
59
|
+
def partial_operator_requirements(operator, lower, upper)
|
|
60
|
+
case operator
|
|
61
|
+
when '>' then [">= #{upper}"]
|
|
62
|
+
when '<=' then ["< #{upper}"]
|
|
63
|
+
when '=' then [">= #{lower}", "< #{upper}"]
|
|
64
|
+
else ["#{operator} #{lower}"]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def hyphen_requirements(low, high)
|
|
69
|
+
requirements = []
|
|
70
|
+
requirements << ">= #{normalize(low)}" unless version_parts(low).empty?
|
|
71
|
+
high_parts = version_parts(high)
|
|
72
|
+
return requirements if high_parts.empty?
|
|
73
|
+
|
|
74
|
+
requirements << (high_parts.length == 3 ? "<= #{normalize(high)}" : "< #{bump(high_parts)}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def caret_requirements(partial)
|
|
78
|
+
parts = version_parts(partial)
|
|
79
|
+
return [] if parts.empty?
|
|
80
|
+
|
|
81
|
+
# Bump the leftmost non-zero part, or the last given part when all are zero.
|
|
82
|
+
index = parts.index(&:positive?) || (parts.length - 1)
|
|
83
|
+
[">= #{normalize(partial)}", "< #{bump(parts.first(index + 1))}"]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def tilde_requirements(partial)
|
|
87
|
+
parts = version_parts(partial)
|
|
88
|
+
return [] if parts.empty?
|
|
89
|
+
|
|
90
|
+
upper = parts.length == 1 ? "#{parts[0] + 1}.0.0" : "#{parts[0]}.#{parts[1] + 1}.0"
|
|
91
|
+
[">= #{normalize(partial)}", "< #{upper}"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def x_range_requirements(partial)
|
|
95
|
+
parts = version_parts(partial)
|
|
96
|
+
return [] if parts.empty?
|
|
97
|
+
return ["= #{normalize(partial)}"] if parts.length == 3 && !partial.match?(/[xX*]/)
|
|
98
|
+
|
|
99
|
+
[">= #{fill(parts)}", "< #{bump(parts)}"]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Numeric leading parts of a version, stopping at the first wildcard.
|
|
103
|
+
# "1.2.x" => [1, 2], "1.2.3-beta" => [1, 2, 3], "x" => []
|
|
104
|
+
def version_parts(partial)
|
|
105
|
+
partial.to_s.sub(/\A[v=]+/, '').sub(/[-+].*\z/, '').split('.').take_while do |part|
|
|
106
|
+
part.match?(/\A\d+\z/)
|
|
107
|
+
end.map(&:to_i)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def fill(parts)
|
|
111
|
+
(parts + [0, 0, 0]).first(3).join('.')
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def bump(parts)
|
|
115
|
+
bumped = parts.dup
|
|
116
|
+
bumped[-1] += 1
|
|
117
|
+
fill(bumped)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Gem::Version understands "1.2.3-beta.1" (as a prerelease) but not build
|
|
121
|
+
# metadata, a leading "v", or missing patch numbers.
|
|
122
|
+
def normalize(version)
|
|
123
|
+
cleaned = version.to_s.sub(/\A[v=]+/, '').sub(/\+.*\z/, '')
|
|
124
|
+
numeric, prerelease = cleaned.split('-', 2)
|
|
125
|
+
numeric = numeric.to_s
|
|
126
|
+
numeric = fill(numeric.split('.').map(&:to_i)) if numeric.match?(/\A\d+(\.\d+){0,2}\z/)
|
|
127
|
+
prerelease ? "#{numeric}-#{prerelease}" : numeric
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def parse_version(version)
|
|
131
|
+
Gem::Version.new(normalize(version))
|
|
132
|
+
rescue ArgumentError
|
|
133
|
+
nil
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -4,6 +4,8 @@ require 'openssl'
|
|
|
4
4
|
|
|
5
5
|
require_relative '../const/file'
|
|
6
6
|
require_relative '../enum/vulnerability_type'
|
|
7
|
+
require_relative 'package_lock_parser'
|
|
8
|
+
require_relative 'semver_range'
|
|
7
9
|
|
|
8
10
|
module Package
|
|
9
11
|
module Audit
|
|
@@ -51,9 +53,16 @@ module Package
|
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
def lockfile_packages
|
|
54
|
-
|
|
55
|
-
return
|
|
56
|
+
yarn_lock = "#{@dir}/#{Const::File::YARN_LOCK}"
|
|
57
|
+
return yarn_lockfile_packages(yarn_lock) if File.exist?(yarn_lock)
|
|
56
58
|
|
|
59
|
+
package_lock = "#{@dir}/#{Const::File::PACKAGE_LOCK_JSON}"
|
|
60
|
+
return PackageLockParser.new(package_lock).all_packages if File.exist?(package_lock)
|
|
61
|
+
|
|
62
|
+
[]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def yarn_lockfile_packages(path)
|
|
57
66
|
packages = []
|
|
58
67
|
current_names = []
|
|
59
68
|
File.foreach(path) do |line|
|
|
@@ -136,16 +145,7 @@ module Package
|
|
|
136
145
|
end
|
|
137
146
|
|
|
138
147
|
def version_matches?(version, vulnerable_versions)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
vulnerable_versions.split(/\s*\|\|\s*/).any? do |range|
|
|
142
|
-
requirements = range.split(/\s+/).reject(&:empty?)
|
|
143
|
-
next true if requirements.empty?
|
|
144
|
-
|
|
145
|
-
Gem::Requirement.new(*requirements).satisfied_by?(Gem::Version.new(version))
|
|
146
|
-
rescue ArgumentError
|
|
147
|
-
true
|
|
148
|
-
end
|
|
148
|
+
SemverRange.new(vulnerable_versions).satisfied_by?(version)
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
def update_vulnerability_info(name, version, severity)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: package-audit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vadim Kononov
|
|
@@ -37,8 +37,9 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '1.2'
|
|
40
|
-
description:
|
|
41
|
-
|
|
40
|
+
description: Audits project dependencies across supported package managers and lists
|
|
41
|
+
those that are outdated, deprecated or have known security vulnerabilities, for
|
|
42
|
+
patch prioritization.
|
|
42
43
|
email:
|
|
43
44
|
- vadim@konoson.com
|
|
44
45
|
executables:
|
|
@@ -73,6 +74,8 @@ files:
|
|
|
73
74
|
- lib/package/audit/models/risk.rb
|
|
74
75
|
- lib/package/audit/npm/node_collection.rb
|
|
75
76
|
- lib/package/audit/npm/npm_meta_data.rb
|
|
77
|
+
- lib/package/audit/npm/package_lock_parser.rb
|
|
78
|
+
- lib/package/audit/npm/semver_range.rb
|
|
76
79
|
- lib/package/audit/npm/vulnerability_finder.rb
|
|
77
80
|
- lib/package/audit/npm/yarn_lock_parser.rb
|
|
78
81
|
- lib/package/audit/ruby/bundler_specs.rb
|
|
@@ -116,5 +119,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
116
119
|
requirements: []
|
|
117
120
|
rubygems_version: 4.0.10
|
|
118
121
|
specification_version: 4
|
|
119
|
-
summary:
|
|
122
|
+
summary: CLI that reports outdated, deprecated and vulnerable dependencies in a project
|
|
120
123
|
test_files: []
|