rubion 0.3.1 → 0.3.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c51023aed1ba275593e8bc5c783d0ee512724b48128ed96d68e433ac7df16f85
4
- data.tar.gz: e31637a3c2be1e60c7e426049d37e6173e5f8a36093a81f53391807538099b4d
3
+ metadata.gz: 72ddffc4eae85bd07e59db90bb20e6890ec3bc3b2da6d725dc9ffea8d7f3e7a5
4
+ data.tar.gz: 4d3fc54759167ab0b568489b61dacacdb7898b0716fb8fde245de186ea1e4995
5
5
  SHA512:
6
- metadata.gz: 4ff1d29b61cac762f306b46751f67c88236575d99323e801021d776fd3b8770685e8821a001bae1c5148d53c4b1884f5dc0952ebbbfef1589b2213ef9f3cad96
7
- data.tar.gz: 23b22a540c176ed00439654959e56f414ebb25fb5c2f8ec0487fad4182d63c4d07ed57be5dda1ffae786f642f277ae86fe604d741b7e7c58fbeee3cbf13bf9a7
6
+ metadata.gz: b3c7dffd83662d797e033288bc326645a14bb77847f16bb1472ab219d75081b4ee97769854a9eac641548c6f601b864cff7bc524d6a64a5605f0bda166f7def1
7
+ data.tar.gz: 253bf224334af3d725c074a7c5f3867ab7cede744663bf46e36a16f74232691648c95210ffc64db673569cb96c1f036152880d7dcb7dac512774824c011874d7
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
- # 🔒 Rubion
1
+ # Rubion
2
2
 
3
3
  **Rubion** is a security and version scanner for Ruby and JavaScript projects. It helps you identify vulnerabilities and outdated dependencies in your Ruby gems and NPM/JavaScript packages.
4
4
 
5
+ <img width="1237" height="671" alt="Screenshot 2025-11-14 at 10 48 12 am" src="https://github.com/user-attachments/assets/a3d93452-c442-416a-9697-de59746e16ad" />
6
+
5
7
  ## Features
6
8
 
7
9
  - 📛 **Gem Vulnerabilities**: Scans for known security vulnerabilities in Ruby gems using `bundle-audit`
@@ -4,8 +4,10 @@ require 'terminal-table'
4
4
 
5
5
  module Rubion
6
6
  class Reporter
7
- def initialize(scan_result)
7
+ def initialize(scan_result, sort_by: 'Behind By(Time)', sort_desc: true)
8
8
  @result = scan_result
9
+ @sort_by = sort_by
10
+ @sort_desc = sort_desc
9
11
  end
10
12
 
11
13
  def report
@@ -36,31 +38,6 @@ module Rubion
36
38
 
37
39
  private
38
40
 
39
- def _print_gem_vulnerabilities
40
- puts "Gem Vulnerabilities:\n\n"
41
-
42
- if @result.gem_vulnerabilities.empty?
43
- puts " ✅ No vulnerabilities found!\n\n"
44
- return
45
- end
46
-
47
- table = Terminal::Table.new do |t|
48
- t.headings = %w[Level Name Version Vulnerability]
49
-
50
- @result.gem_vulnerabilities.each do |vuln|
51
- t.add_row [
52
- severity_with_icon(vuln[:severity]),
53
- vuln[:gem],
54
- vuln[:version],
55
- truncate(vuln[:title], 50)
56
- ]
57
- end
58
- end
59
-
60
- puts table
61
- puts "\n"
62
- end
63
-
64
41
  def print_header
65
42
  # Simplified header
66
43
  puts "\n"
@@ -97,10 +74,14 @@ module Rubion
97
74
  return
98
75
  end
99
76
 
77
+ # Sort if sort_by is specified
78
+ versions = @result.gem_versions.dup
79
+ versions = sort_versions(versions, :gem) if @sort_by
80
+
100
81
  table = Terminal::Table.new do |t|
101
82
  t.headings = ['Name', 'Current', 'Date', 'Latest', 'Date', 'Behind By(Time)', 'Behind By(Versions)']
102
83
 
103
- @result.gem_versions.each do |gem|
84
+ versions.each do |gem|
104
85
  t.add_row [
105
86
  gem[:gem],
106
87
  gem[:current],
@@ -150,10 +131,14 @@ module Rubion
150
131
  return
151
132
  end
152
133
 
134
+ # Sort if sort_by is specified
135
+ versions = @result.package_versions.dup
136
+ versions = sort_versions(versions, :package) if @sort_by
137
+
153
138
  table = Terminal::Table.new do |t|
154
- t.headings = ['Name', 'Current', 'Date', 'Latest', 'Date', 'Behind By', 'Versions']
139
+ t.headings = ['Name', 'Current', 'Date', 'Latest', 'Date', 'Behind By(Time)', 'Behind By(Versions)']
155
140
 
156
- @result.package_versions.each do |pkg|
141
+ versions.each do |pkg|
157
142
  t.add_row [
158
143
  pkg[:package],
159
144
  pkg[:current],
@@ -218,7 +203,7 @@ module Rubion
218
203
  def truncate(text, length = 50)
219
204
  return text if text.length <= length
220
205
 
221
- "#{text[0..length - 3]}..."
206
+ "#{text[0..(length - 3)]}..."
222
207
  end
223
208
 
224
209
  def version_difference(current, latest)
@@ -242,5 +227,107 @@ module Rubion
242
227
  rescue StandardError
243
228
  'unknown'
244
229
  end
230
+
231
+ # Sort versions array based on the specified column
232
+ def sort_versions(versions, name_key)
233
+ return versions unless @sort_by
234
+
235
+ column = @sort_by.strip.downcase
236
+ name_key_sym = name_key # :gem or :package
237
+
238
+ # Normalize column name - default to 'name' if not recognized
239
+ normalized_column = case column
240
+ when 'name', 'current', 'date', 'latest',
241
+ 'behind by(time)', 'behind by time', 'time',
242
+ 'behind by(versions)', 'behind by versions', 'versions'
243
+ column
244
+ else
245
+ 'name' # Default to name sorting
246
+ end
247
+
248
+ sorted = versions.sort_by do |item|
249
+ case normalized_column
250
+ when 'name'
251
+ item[name_key_sym].to_s.downcase
252
+ when 'current'
253
+ parse_version_for_sort(item[:current])
254
+ when 'date'
255
+ # Sort by current_date (first Date column)
256
+ parse_date_for_sort(item[:current_date])
257
+ when 'latest'
258
+ parse_version_for_sort(item[:latest])
259
+ when 'behind by(time)', 'behind by time', 'time'
260
+ parse_time_for_sort(item[:time_diff])
261
+ when 'behind by(versions)', 'behind by versions', 'versions'
262
+ parse_version_count_for_sort(item[:version_count])
263
+ end
264
+ end
265
+
266
+ # Reverse if descending order requested
267
+ @sort_desc ? sorted.reverse : sorted
268
+ end
269
+
270
+ # Parse version string for sorting (handles semantic versions)
271
+ def parse_version_for_sort(version_str)
272
+ return [0, 0, 0, ''] if version_str.nil? || version_str == 'N/A' || version_str == 'unknown'
273
+
274
+ # Handle version strings like "1.2.3", "1.2.3.4", "1.2.3-beta", etc.
275
+ parts = version_str.to_s.split(/[.-]/)
276
+ major = parts[0].to_i
277
+ minor = parts[1].to_i
278
+ patch = parts[2].to_i
279
+ suffix = parts[3..-1].join('') if parts.length > 3
280
+
281
+ [major, minor, patch, suffix || '']
282
+ end
283
+
284
+ # Parse date string for sorting (handles M/D/YYYY format)
285
+ def parse_date_for_sort(date_str)
286
+ return [0, 0, 0] if date_str.nil? || date_str == 'N/A'
287
+
288
+ begin
289
+ parts = date_str.split('/').map(&:to_i)
290
+ return [parts[2] || 0, parts[0] || 0, parts[1] || 0] if parts.length == 3
291
+ rescue StandardError
292
+ # If parsing fails, return a date that sorts last
293
+ end
294
+
295
+ [0, 0, 0]
296
+ end
297
+
298
+ # Parse time difference string for sorting (e.g., "1 year", "3 months", "5 days")
299
+ def parse_time_for_sort(time_str)
300
+ return [0, 0] if time_str.nil? || time_str == 'N/A'
301
+
302
+ time_str = time_str.to_s.downcase.strip
303
+
304
+ # Extract number and unit
305
+ match = time_str.match(/(\d+(?:\.\d+)?)\s*(year|month|day|years|months|days)/)
306
+ return [0, 0] unless match
307
+
308
+ value = match[1].to_f
309
+ unit = match[2].to_s.downcase
310
+
311
+ # Convert to days for comparison
312
+ days = case unit
313
+ when /year/
314
+ value * 365
315
+ when /month/
316
+ value * 30
317
+ when /day/
318
+ value
319
+ else
320
+ 0
321
+ end
322
+
323
+ [days.to_i, value]
324
+ end
325
+
326
+ # Parse version count for sorting
327
+ def parse_version_count_for_sort(count)
328
+ return 0 if count.nil? || count == 'N/A' || count == 'unknown'
329
+
330
+ count.to_i
331
+ end
245
332
  end
246
333
  end
@@ -37,7 +37,7 @@ module Rubion
37
37
  @result
38
38
  end
39
39
 
40
- def scan_incremental(options = { gems: true, packages: true })
40
+ def scan_incremental(options = { gems: true, packages: true, sort_by: "Behind By(Time)", sort_desc: true })
41
41
  puts "🔍 Scanning project at: #{@project_path}\n\n"
42
42
 
43
43
  # Scan and display Ruby gems first (if enabled)
@@ -46,7 +46,7 @@ module Rubion
46
46
 
47
47
  # Print gem results immediately
48
48
  puts "\n"
49
- reporter = Reporter.new(@result)
49
+ reporter = Reporter.new(@result, sort_by: options[:sort_by], sort_desc: options[:sort_desc])
50
50
  reporter.print_gem_vulnerabilities
51
51
  reporter.print_gem_versions
52
52
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubion
4
- VERSION = "0.3.1"
4
+ VERSION = "0.3.3"
5
5
  end
6
6
 
data/lib/rubion.rb CHANGED
@@ -28,7 +28,8 @@ module Rubion
28
28
  end
29
29
 
30
30
  def self.parse_scan_options(args)
31
- options = { gems: true, packages: true }
31
+ # Default to sorting by "Behind By(Time)" in descending order
32
+ options = { gems: true, packages: true, sort_by: "Behind By(Time)", sort_desc: true }
32
33
 
33
34
  # Check for --gems-only or --packages-only flags
34
35
  if args.include?('--gems-only') || args.include?('-g')
@@ -43,20 +44,31 @@ module Rubion
43
44
  options[:packages] = args.include?('--packages')
44
45
  end
45
46
 
47
+ # Parse --sort-by or -s option
48
+ sort_index = args.index('--sort-by') || args.index('-s')
49
+ if sort_index && args[sort_index + 1]
50
+ options[:sort_by] = args[sort_index + 1]
51
+ end
52
+
53
+ # Parse --asc or --ascending for ascending order (descending is default)
54
+ options[:sort_desc] = false if args.include?('--asc') || args.include?('--ascending')
55
+
46
56
  options
47
57
  end
48
58
 
49
- def self.scan(options = { gems: true, packages: true })
59
+ def self.scan(options = { gems: true, packages: true, sort_by: "Behind By(Time)", sort_desc: true })
50
60
  project_path = Dir.pwd
51
61
 
52
62
  scanner = Scanner.new(project_path: project_path)
53
63
  result = scanner.scan_incremental(options)
54
64
 
55
65
  # Results are already printed incrementally based on options
56
- reporter = Reporter.new(result)
57
-
58
- # Only print package results if packages were scanned
66
+ # Package results are printed in scan_incremental, but we need to ensure
67
+ # they use the same reporter instance with sort_by
68
+ # Actually, scan_incremental handles gem printing, but package printing
69
+ # happens here, so we need a reporter for packages
59
70
  if options[:packages]
71
+ reporter = Reporter.new(result, sort_by: options[:sort_by], sort_desc: options[:sort_desc])
60
72
  reporter.print_package_vulnerabilities
61
73
  reporter.print_package_versions
62
74
  end
@@ -76,6 +88,9 @@ module Rubion
76
88
  --gems, --gem, -g Scan only Ruby gems (skip NPM packages)
77
89
  --packages, --npm, -p Scan only NPM packages (skip Ruby gems)
78
90
  --all, -a Scan both gems and packages (default)
91
+ --sort-by COLUMN, -s COLUMN Sort results by column (Name, Current, Date, Latest, Behind By(Time), Behind By(Versions))
92
+ (default: "Behind By(Time)" in descending order)
93
+ --asc, --ascending Sort in ascending order (use with --sort-by)
79
94
 
80
95
  DESCRIPTION:
81
96
  Rubion scans your project for:
@@ -101,6 +116,18 @@ module Rubion
101
116
  # Scan only NPM packages
102
117
  rubion scan --packages
103
118
 
119
+ # Sort by name
120
+ rubion scan --sort-by Name
121
+
122
+ # Sort by versions behind
123
+ rubion scan -s "Behind By(Versions)"
124
+
125
+ # Sort by name in descending order (default)
126
+ rubion scan --sort-by Name
127
+
128
+ # Sort by name in ascending order
129
+ rubion scan --sort-by Name --asc
130
+
104
131
  # Get help
105
132
  rubion help
106
133
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - bipashant