still_active 1.4.2 → 1.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 +20 -0
- data/README.md +100 -19
- data/lib/helpers/alternatives_helper.rb +42 -0
- data/lib/helpers/bot_context.rb +132 -0
- data/lib/helpers/catalog_index.rb +98 -0
- data/lib/helpers/cyclonedx_helper.rb +159 -0
- data/lib/helpers/markdown_helper.rb +20 -2
- data/lib/helpers/ruby_advisory_db.rb +93 -0
- data/lib/helpers/sarif_helper.rb +9 -2
- data/lib/helpers/terminal_helper.rb +27 -3
- data/lib/helpers/version_helper.rb +9 -0
- data/lib/helpers/vulnerability_helper.rb +35 -0
- data/lib/still_active/cli.rb +55 -4
- data/lib/still_active/config.rb +7 -1
- data/lib/still_active/deps_dev_client.rb +1 -0
- data/lib/still_active/options.rb +12 -0
- data/lib/still_active/version.rb +1 -1
- data/lib/still_active/workflow.rb +35 -7
- data/still_active.gemspec +6 -1
- metadata +22 -3
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
require_relative "deps_dev_client"
|
|
4
4
|
require_relative "gitlab_client"
|
|
5
5
|
require_relative "repository"
|
|
6
|
+
require_relative "../helpers/activity_helper"
|
|
7
|
+
require_relative "../helpers/alternatives_helper"
|
|
8
|
+
require_relative "../helpers/catalog_index"
|
|
6
9
|
require_relative "../helpers/libyear_helper"
|
|
10
|
+
require_relative "../helpers/ruby_advisory_db"
|
|
7
11
|
require_relative "../helpers/ruby_helper"
|
|
8
12
|
require_relative "../helpers/version_helper"
|
|
13
|
+
require_relative "../helpers/vulnerability_helper"
|
|
9
14
|
require "async"
|
|
10
15
|
require "async/barrier"
|
|
11
16
|
require "async/semaphore"
|
|
@@ -17,6 +22,10 @@ module StillActive
|
|
|
17
22
|
|
|
18
23
|
def call(&on_progress)
|
|
19
24
|
task = Async do
|
|
25
|
+
# Load the optional ruby-advisory-db once, before the fan-out, so the
|
|
26
|
+
# read-only Database is shared across fibers rather than reloaded per gem.
|
|
27
|
+
advisory_db = RubyAdvisoryDb.load
|
|
28
|
+
catalog = StillActive.config.alternatives ? CatalogIndex.load : nil
|
|
20
29
|
barrier = Async::Barrier.new
|
|
21
30
|
semaphore = Async::Semaphore.new(StillActive.config.parallelism, parent: barrier)
|
|
22
31
|
result_object = {}
|
|
@@ -30,6 +39,8 @@ module StillActive
|
|
|
30
39
|
gem_version: gem[:version],
|
|
31
40
|
source_type: gem[:source_type] || :rubygems,
|
|
32
41
|
source_uri: gem[:source_uri],
|
|
42
|
+
advisory_db: advisory_db,
|
|
43
|
+
catalog: catalog,
|
|
33
44
|
)
|
|
34
45
|
rescue Octokit::TooManyRequests
|
|
35
46
|
$stderr.print("\r\e[K") if on_progress
|
|
@@ -54,24 +65,27 @@ module StillActive
|
|
|
54
65
|
|
|
55
66
|
private
|
|
56
67
|
|
|
57
|
-
def gem_info(gem_name:, result_object:, gem_version: nil, source_type: :rubygems, source_uri: nil)
|
|
68
|
+
def gem_info(gem_name:, result_object:, gem_version: nil, source_type: :rubygems, source_uri: nil, advisory_db: nil, catalog: nil)
|
|
58
69
|
result_object[gem_name] = { source_type: source_type }
|
|
59
70
|
result_object[gem_name][:version_used] = gem_version if gem_version
|
|
60
71
|
|
|
61
72
|
case source_type
|
|
62
73
|
when :path, :git
|
|
63
|
-
gem_info_non_rubygems(gem_name: gem_name, gem_version: gem_version, result_object: result_object, source_uri: source_uri)
|
|
74
|
+
gem_info_non_rubygems(gem_name: gem_name, gem_version: gem_version, result_object: result_object, source_uri: source_uri, advisory_db: advisory_db)
|
|
64
75
|
else
|
|
65
76
|
gem_info_rubygems(
|
|
66
77
|
gem_name: gem_name,
|
|
67
78
|
gem_version: gem_version,
|
|
68
79
|
result_object: result_object,
|
|
69
80
|
source_uri: source_uri,
|
|
81
|
+
advisory_db: advisory_db,
|
|
70
82
|
)
|
|
71
83
|
end
|
|
84
|
+
|
|
85
|
+
attach_alternatives(gem_name: gem_name, result_object: result_object, catalog: catalog)
|
|
72
86
|
end
|
|
73
87
|
|
|
74
|
-
def gem_info_rubygems(gem_name:, gem_version:, result_object:, source_uri:)
|
|
88
|
+
def gem_info_rubygems(gem_name:, gem_version:, result_object:, source_uri:, advisory_db: nil)
|
|
75
89
|
vs = versions(gem_name: gem_name, source_uri: source_uri)
|
|
76
90
|
repo_info = repository_info(gem_name: gem_name, versions: vs)
|
|
77
91
|
commit_date = last_commit_date(
|
|
@@ -89,6 +103,7 @@ module StillActive
|
|
|
89
103
|
deps_dev = fetch_deps_dev_info(
|
|
90
104
|
gem_name: gem_name,
|
|
91
105
|
version: gem_version || VersionHelper.gem_version(version_hash: last_release),
|
|
106
|
+
advisory_db: advisory_db,
|
|
92
107
|
)
|
|
93
108
|
result_object[gem_name].merge!({
|
|
94
109
|
latest_version: VersionHelper.gem_version(version_hash: last_release),
|
|
@@ -118,6 +133,7 @@ module StillActive
|
|
|
118
133
|
|
|
119
134
|
version_used_release_date: VersionHelper.release_date(version_hash: version_used),
|
|
120
135
|
version_yanked: !vs.empty? && version_used.nil?,
|
|
136
|
+
license: VersionHelper.license(version_hash: version_used),
|
|
121
137
|
libyear: LibyearHelper.gem_libyear(
|
|
122
138
|
version_used_release_date: VersionHelper.release_date(version_hash: version_used),
|
|
123
139
|
latest_version_release_date: VersionHelper.release_date(version_hash: last_release),
|
|
@@ -126,10 +142,10 @@ module StillActive
|
|
|
126
142
|
end
|
|
127
143
|
end
|
|
128
144
|
|
|
129
|
-
def gem_info_non_rubygems(gem_name:, gem_version:, result_object:, source_uri: nil)
|
|
145
|
+
def gem_info_non_rubygems(gem_name:, gem_version:, result_object:, source_uri: nil, advisory_db: nil)
|
|
130
146
|
repo_info = repository_info_for_non_rubygems(gem_name: gem_name, source_uri: source_uri)
|
|
131
147
|
source, owner, name = repo_info.values_at(:source, :owner, :name)
|
|
132
|
-
deps_dev = gem_version ? fetch_deps_dev_info(gem_name: gem_name, version: gem_version) : {}
|
|
148
|
+
deps_dev = gem_version ? fetch_deps_dev_info(gem_name: gem_name, version: gem_version, advisory_db: advisory_db) : {}
|
|
133
149
|
|
|
134
150
|
# Fall back to repo-derived project_id for scorecard when deps.dev doesn't have the version
|
|
135
151
|
deps_dev[:scorecard_score] ||= DepsDevClient.project_scorecard(project_id: repo_info[:project_id])&.dig(:score)
|
|
@@ -142,11 +158,23 @@ module StillActive
|
|
|
142
158
|
})
|
|
143
159
|
end
|
|
144
160
|
|
|
145
|
-
def
|
|
161
|
+
def attach_alternatives(gem_name:, result_object:, catalog:)
|
|
162
|
+
return if catalog.nil?
|
|
163
|
+
return unless [:archived, :critical].include?(ActivityHelper.activity_level(result_object[gem_name]))
|
|
164
|
+
|
|
165
|
+
leads = AlternativesHelper.leads_for(gem_name: gem_name, index: catalog)
|
|
166
|
+
result_object[gem_name][:alternatives] = leads unless leads.empty?
|
|
167
|
+
rescue StandardError
|
|
168
|
+
nil # cosmetic best-effort: lead-fetching must never break the core audit
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def fetch_deps_dev_info(gem_name:, version:, advisory_db: nil)
|
|
146
172
|
info = DepsDevClient.version_info(gem_name: gem_name, version: version)
|
|
147
173
|
scorecard = DepsDevClient.project_scorecard(project_id: info&.dig(:project_id))
|
|
148
174
|
advisory_keys = info&.dig(:advisory_keys) || []
|
|
149
|
-
|
|
175
|
+
deps_dev_vulns = advisory_keys.filter_map { |id| DepsDevClient.advisory_detail(advisory_id: id) }
|
|
176
|
+
radb_vulns = RubyAdvisoryDb.advisories_for(database: advisory_db, gem_name: gem_name, version: version)
|
|
177
|
+
vulnerabilities = VulnerabilityHelper.merge_advisories(deps_dev: deps_dev_vulns, ruby_advisory_db: radb_vulns)
|
|
150
178
|
{
|
|
151
179
|
scorecard_score: scorecard&.dig(:score),
|
|
152
180
|
vulnerability_count: vulnerabilities.length,
|
data/still_active.gemspec
CHANGED
|
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
spec.executables = spec.files.grep(%r{\Abin/still_active}) { |f| File.basename(f) }
|
|
37
37
|
spec.require_paths = ["lib"]
|
|
38
38
|
|
|
39
|
+
spec.add_development_dependency("bundler-audit")
|
|
39
40
|
spec.add_development_dependency("debug")
|
|
40
41
|
spec.add_development_dependency("faker")
|
|
41
42
|
spec.add_development_dependency("json_schemer")
|
|
@@ -44,7 +45,11 @@ Gem::Specification.new do |spec|
|
|
|
44
45
|
spec.add_development_dependency("rubocop-rspec")
|
|
45
46
|
spec.add_development_dependency("rubocop-shopify")
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
# 2.0/2.1 ship a scheduler that breaks our fan-out (io_read); 2.2 is the
|
|
49
|
+
# verified floor (checked against Ruby 3.3 in Docker). octokit/faraday-retry/
|
|
50
|
+
# gems work down to ancient versions, so they stay unpinned rather than
|
|
51
|
+
# carry an artificial floor.
|
|
52
|
+
spec.add_runtime_dependency("async", ">= 2.2")
|
|
48
53
|
spec.add_runtime_dependency("bundler", ">= 2.0")
|
|
49
54
|
spec.add_runtime_dependency("faraday-retry")
|
|
50
55
|
spec.add_runtime_dependency("gems")
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: still_active
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean Floyd
|
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: bundler-audit
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: debug
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -113,14 +127,14 @@ dependencies:
|
|
|
113
127
|
requirements:
|
|
114
128
|
- - ">="
|
|
115
129
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '
|
|
130
|
+
version: '2.2'
|
|
117
131
|
type: :runtime
|
|
118
132
|
prerelease: false
|
|
119
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
134
|
requirements:
|
|
121
135
|
- - ">="
|
|
122
136
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '
|
|
137
|
+
version: '2.2'
|
|
124
138
|
- !ruby/object:Gem::Dependency
|
|
125
139
|
name: bundler
|
|
126
140
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -197,14 +211,19 @@ files:
|
|
|
197
211
|
- README.md
|
|
198
212
|
- bin/still_active
|
|
199
213
|
- lib/helpers/activity_helper.rb
|
|
214
|
+
- lib/helpers/alternatives_helper.rb
|
|
200
215
|
- lib/helpers/ansi_helper.rb
|
|
216
|
+
- lib/helpers/bot_context.rb
|
|
201
217
|
- lib/helpers/bundler_helper.rb
|
|
218
|
+
- lib/helpers/catalog_index.rb
|
|
219
|
+
- lib/helpers/cyclonedx_helper.rb
|
|
202
220
|
- lib/helpers/diff_markdown_helper.rb
|
|
203
221
|
- lib/helpers/emoji_helper.rb
|
|
204
222
|
- lib/helpers/http_helper.rb
|
|
205
223
|
- lib/helpers/libyear_helper.rb
|
|
206
224
|
- lib/helpers/lockfile_indexer.rb
|
|
207
225
|
- lib/helpers/markdown_helper.rb
|
|
226
|
+
- lib/helpers/ruby_advisory_db.rb
|
|
208
227
|
- lib/helpers/ruby_helper.rb
|
|
209
228
|
- lib/helpers/sarif_helper.rb
|
|
210
229
|
- lib/helpers/terminal_helper.rb
|