lockfile_audit 0.1.1 → 0.2.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 +21 -0
- data/README.md +6 -8
- data/lib/lockfile_audit/version.rb +1 -1
- data/lib/lockfile_audit/vulnerability_collector.rb +49 -92
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f54f64dd70f25a217ccd3c29bd0ea985f420f3d947074a57b033f711950d08df
|
|
4
|
+
data.tar.gz: bd3edfc6cb9c823955aa7e81d895f36f1207a95475c4dd4b5cd4c05ec29ff496
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 46de93997759fd386a2f11bf5aa6520c640299b4faaf8d0f4a7e82cfebfc52617f5577b25bffa9ffece99bd589108905d3dcea5ed10112a9134a193fbf344e30
|
|
7
|
+
data.tar.gz: 0342efc962e99aab09b78aa848028eab33bafbba188f148227607ac90ab015b370ab5b1029711e2de01cd256286e311100aeeb2bffbe9f7ae1d61ecc5b785339
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-10-04
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- `VulnerabilityCollector` now scans the lockfile in-process with `Bundler::Audit::Scanner` instead of spawning the `bundle-audit` executable in a child Ruby process. The subprocess approach failed with `Bundler::GemfileError ... is already registered` on machines that have more than one Bundler version installed, because two copies of Bundler were loaded into the child process.
|
|
14
|
+
|
|
15
|
+
- `bundler-audit` must now be in the host application's bundle (add `gem "bundler-audit"` to your Gemfile). Having it installed globally or on `PATH` is no longer enough. Passing audit_json: still works without bundler-audit.
|
|
16
|
+
|
|
17
|
+
- A failing scan now raises `Errors::BundlerAuditFailed` with the underlying error and a hint to run `bundle-audit update`. `Errors::BundlerAuditNotInstalled` is raised only when `bundler-audit` cannot be loaded.
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
- The subprocess code path and its environment scrubbing (`BUNDLER_ENV_KEYS`, `Open3`, `RbConfig.ruby` / `Gem.bin_path` lookup).
|
|
21
|
+
|
|
22
|
+
### Notes
|
|
23
|
+
- Insecure-source findings from `bundler-audit` (for example an `http://` gem source) are not included in `audit.vulnerabilities`; only advisories for unpatched gems are reported.
|
|
24
|
+
|
|
25
|
+
## [0.1.2] - 2026-10-03
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Source moved to the `betterwebsites/ror_gems` monorepo. Gemspec metadata
|
|
30
|
+
links updated. No code changes.
|
|
10
31
|
## [0.1.1] - 2026-09-26
|
|
11
32
|
|
|
12
33
|
### Fixed
|
data/README.md
CHANGED
|
@@ -10,6 +10,8 @@ feed into a dashboard, or archive for compliance.
|
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
|
+
`gem "bundler-audit"` must be in the app's Gemfile
|
|
14
|
+
|
|
13
15
|
Add it to your Gemfile:
|
|
14
16
|
|
|
15
17
|
gem "lockfile_audit"
|
|
@@ -116,16 +118,12 @@ All errors inherit from `LockfileAudit::Errors::Error`.
|
|
|
116
118
|
|
|
117
119
|
## Development
|
|
118
120
|
|
|
121
|
+
cd lockfile_audit
|
|
119
122
|
bundle install
|
|
120
|
-
bundle exec
|
|
121
|
-
bundle exec standardrb
|
|
122
|
-
|
|
123
|
-
To release a new version:
|
|
123
|
+
bundle exec rake
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
3. `gem build lockfile_audit.gemspec`
|
|
128
|
-
4. `gem push lockfile_audit-<version>.gem`
|
|
125
|
+
To release, see the "Releasing a gem" section of the
|
|
126
|
+
[repo README](../README.md).
|
|
129
127
|
|
|
130
128
|
## License
|
|
131
129
|
|
|
@@ -1,53 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
-
require "open3"
|
|
5
4
|
|
|
6
5
|
module LockfileAudit
|
|
7
6
|
# Collects vulnerability findings from bundler-audit.
|
|
8
7
|
#
|
|
9
|
-
# Either accepts pre-computed JSON (via +audit_json:+) or
|
|
10
|
-
#
|
|
8
|
+
# Either accepts pre-computed JSON (via +audit_json:+) or, when no JSON is
|
|
9
|
+
# supplied, scans the lockfile in-process using Bundler::Audit::Scanner.
|
|
11
10
|
#
|
|
12
|
-
#
|
|
11
|
+
# Scanning in-process (instead of spawning the `bundle-audit` executable)
|
|
12
|
+
# avoids loading a second copy of Bundler in a child Ruby process, which
|
|
13
|
+
# fails on machines that have more than one Bundler version installed.
|
|
14
|
+
#
|
|
15
|
+
# Requirements for the in-process scan:
|
|
16
|
+
# - the `bundler-audit` gem must be in the host application's bundle
|
|
17
|
+
# - the advisory database must exist (run `bundle-audit update`)
|
|
18
|
+
#
|
|
19
|
+
# For +audit_json:+ the normalizer tolerates both known shapes of
|
|
20
|
+
# bundler-audit JSON output:
|
|
13
21
|
# - a bare Array of findings (newer versions)
|
|
14
22
|
# - a Hash with a "results" key holding the Array (older versions)
|
|
15
|
-
#
|
|
16
|
-
# When invoking the CLI, the collector runs with a scrubbed environment so
|
|
17
|
-
# the parent process's Bundler state (BUNDLE_*, RUBYOPT, etc.) does not leak
|
|
18
|
-
# into the subprocess. It also invokes bundler-audit using the *current*
|
|
19
|
-
# Ruby (RbConfig.ruby) and the exact installed binary path (Gem.bin_path),
|
|
20
|
-
# so the subprocess uses the same Ruby as the host application.
|
|
21
23
|
class VulnerabilityCollector
|
|
22
|
-
# Environment variables that must not leak into the bundler-audit
|
|
23
|
-
# subprocess. These are set by Bundler when the parent process is a
|
|
24
|
-
# bundled Ruby app, and their presence forces Bundler to re-initialize
|
|
25
|
-
# inside the subprocess.
|
|
26
|
-
BUNDLER_ENV_KEYS = %w[
|
|
27
|
-
BUNDLE_GEMFILE
|
|
28
|
-
BUNDLE_BIN_PATH
|
|
29
|
-
BUNDLE_PATH
|
|
30
|
-
BUNDLE_APP_CONFIG
|
|
31
|
-
BUNDLE_FROZEN
|
|
32
|
-
BUNDLE_WITHOUT
|
|
33
|
-
BUNDLE_WITH
|
|
34
|
-
BUNDLE_JOBS
|
|
35
|
-
BUNDLE_RETRY
|
|
36
|
-
BUNDLER_VERSION
|
|
37
|
-
BUNDLER_ORIG_BUNDLE_BIN_PATH
|
|
38
|
-
BUNDLER_ORIG_BUNDLE_GEMFILE
|
|
39
|
-
BUNDLER_ORIG_BUNDLE_PATH
|
|
40
|
-
BUNDLER_ORIG_MANPATH
|
|
41
|
-
BUNDLER_ORIG_RUBYLIB
|
|
42
|
-
BUNDLER_ORIG_RUBYOPT
|
|
43
|
-
RUBYOPT
|
|
44
|
-
RUBYLIB
|
|
45
|
-
].freeze
|
|
46
|
-
|
|
47
24
|
# @param audit_json [String, nil] raw JSON from bundler-audit; if nil,
|
|
48
|
-
#
|
|
49
|
-
# @param lockfile_path [String, Pathname] path to Gemfile.lock
|
|
50
|
-
# derive the working directory for the CLI invocation
|
|
25
|
+
# the lockfile is scanned in-process
|
|
26
|
+
# @param lockfile_path [String, Pathname] path to Gemfile.lock
|
|
51
27
|
def initialize(audit_json: nil, lockfile_path: "Gemfile.lock")
|
|
52
28
|
@audit_json = audit_json
|
|
53
29
|
@lockfile_path = lockfile_path
|
|
@@ -55,13 +31,14 @@ module LockfileAudit
|
|
|
55
31
|
|
|
56
32
|
# @return [Array<Hash>] normalized findings, each with keys
|
|
57
33
|
# `:gem`, `:version`, `:criticality`, `:description`
|
|
58
|
-
# @raise [Errors::InvalidAuditJson] if the JSON cannot be parsed
|
|
59
|
-
# @raise [Errors::BundlerAuditNotInstalled] if bundler-audit
|
|
34
|
+
# @raise [Errors::InvalidAuditJson] if the supplied JSON cannot be parsed
|
|
35
|
+
# @raise [Errors::BundlerAuditNotInstalled] if bundler-audit cannot be loaded
|
|
36
|
+
# @raise [Errors::BundlerAuditFailed] if the in-process scan raises
|
|
60
37
|
def call
|
|
61
|
-
|
|
62
|
-
return [] if
|
|
38
|
+
return scan_in_process if @audit_json.nil?
|
|
39
|
+
return [] if @audit_json.strip.empty?
|
|
63
40
|
|
|
64
|
-
parsed = JSON.parse(
|
|
41
|
+
parsed = JSON.parse(@audit_json)
|
|
65
42
|
results = parsed.is_a?(Hash) ? parsed.fetch("results", []) : parsed
|
|
66
43
|
|
|
67
44
|
Array(results).map { |finding| normalize(finding) }
|
|
@@ -71,62 +48,42 @@ module LockfileAudit
|
|
|
71
48
|
|
|
72
49
|
private
|
|
73
50
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
51
|
+
# Runs Bundler::Audit::Scanner against the lockfile and maps unpatched
|
|
52
|
+
# gem results onto the public schema. Insecure-source results (e.g. an
|
|
53
|
+
# http:// gem source) have no gem or advisory and are skipped.
|
|
54
|
+
def scan_in_process
|
|
55
|
+
require "bundler/audit/scanner"
|
|
77
56
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
raise Errors::BundlerAuditNotInstalled,
|
|
81
|
-
"bundler-audit is not installed for #{RbConfig.ruby} (#{RUBY_VERSION}). " \
|
|
82
|
-
"Install it for this Ruby: gem install bundler-audit"
|
|
83
|
-
rescue Errno::ENOENT
|
|
84
|
-
raise_not_installed(nil)
|
|
85
|
-
end
|
|
57
|
+
path = File.expand_path(@lockfile_path)
|
|
58
|
+
scanner = Bundler::Audit::Scanner.new(File.dirname(path), File.basename(path))
|
|
86
59
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
clean_env,
|
|
90
|
-
RbConfig.ruby, bundle_audit_path, "check", "--format", "json",
|
|
91
|
-
chdir: File.dirname(File.expand_path(@lockfile_path))
|
|
92
|
-
)
|
|
93
|
-
end
|
|
60
|
+
results = []
|
|
61
|
+
scanner.scan { |result| results << result }
|
|
94
62
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
end
|
|
63
|
+
results.filter_map do |result|
|
|
64
|
+
next unless result.is_a?(Bundler::Audit::Results::UnpatchedGem)
|
|
98
65
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
66
|
+
advisory = result.advisory
|
|
67
|
+
{
|
|
68
|
+
gem: result.gem.name,
|
|
69
|
+
version: result.gem.version.to_s,
|
|
70
|
+
criticality: advisory.criticality&.to_s&.capitalize,
|
|
71
|
+
description: advisory.title
|
|
72
|
+
}
|
|
104
73
|
end
|
|
74
|
+
rescue LoadError
|
|
75
|
+
raise Errors::BundlerAuditNotInstalled,
|
|
76
|
+
"bundler-audit is not available. " \
|
|
77
|
+
"Add `gem \"bundler-audit\"` to your Gemfile and run `bundle install`."
|
|
78
|
+
rescue Errors::Error
|
|
79
|
+
raise
|
|
80
|
+
rescue => e
|
|
81
|
+
raise Errors::BundlerAuditFailed,
|
|
82
|
+
"bundler-audit scan failed: #{e.class}: #{e.message}. " \
|
|
83
|
+
"Run `bundle-audit update` to fetch the advisory database."
|
|
105
84
|
end
|
|
106
85
|
|
|
107
|
-
# bundler-audit
|
|
108
|
-
# us), and 0 when the lockfile is clean. Any other exit code, or empty
|
|
109
|
-
# stdout, means the invocation didn't produce usable output.
|
|
110
|
-
def usable?(stdout, status)
|
|
111
|
-
return false if stdout.nil? || stdout.strip.empty?
|
|
112
|
-
return true if [0, 1].include?(status.exitstatus)
|
|
113
|
-
|
|
114
|
-
false
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# Raises BundlerAuditNotInstalled with an actionable message that
|
|
118
|
-
# includes stderr when available.
|
|
119
|
-
def raise_not_installed(stderr)
|
|
120
|
-
detail = stderr.to_s.strip
|
|
121
|
-
message = "bundler-audit could not be invoked. " \
|
|
122
|
-
"Ensure the `bundler-audit` gem is installed for this Ruby " \
|
|
123
|
-
"(#{RUBY_VERSION}) and run `bundle-audit update` to fetch the " \
|
|
124
|
-
"advisory database."
|
|
125
|
-
message += " stderr: #{detail}" unless detail.empty?
|
|
126
|
-
raise Errors::BundlerAuditNotInstalled, message
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Maps a single bundler-audit finding onto the public schema.
|
|
86
|
+
# Maps a single bundler-audit JSON finding onto the public schema.
|
|
130
87
|
#
|
|
131
88
|
# bundler-audit has used slightly different key names across versions, so
|
|
132
89
|
# we fall back to nested "advisory" keys when the top-level ones are absent.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lockfile_audit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- behnam1369
|
|
@@ -27,12 +27,13 @@ files:
|
|
|
27
27
|
- lib/lockfile_audit/report.rb
|
|
28
28
|
- lib/lockfile_audit/version.rb
|
|
29
29
|
- lib/lockfile_audit/vulnerability_collector.rb
|
|
30
|
-
homepage: https://github.com/
|
|
30
|
+
homepage: https://github.com/betterwebsites/ror_gems/tree/main/lockfile_audit
|
|
31
31
|
licenses:
|
|
32
32
|
- MIT
|
|
33
33
|
metadata:
|
|
34
|
-
homepage_uri: https://github.com/
|
|
35
|
-
|
|
34
|
+
homepage_uri: https://github.com/betterwebsites/ror_gems/tree/main/lockfile_audit
|
|
35
|
+
source_code_uri: https://github.com/betterwebsites/ror_gems/tree/main/lockfile_audit
|
|
36
|
+
changelog_uri: https://github.com/betterwebsites/ror_gems/blob/main/lockfile_audit/CHANGELOG.md
|
|
36
37
|
rdoc_options: []
|
|
37
38
|
require_paths:
|
|
38
39
|
- lib
|