grim-reaper 1.0.29 ā 1.0.33
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/bin/grim +168 -32
- data/lib/grim_reaper/core.rb +14 -0
- data/lib/grim_reaper/otp_module.rb +275 -0
- data/lib/grim_reaper/version.rb +1 -1
- data/lib/grim_reaper.rb +9 -0
- metadata +17 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf90fc5100b580ffa60500f336ca9133ef028fc34256926c6c275a8701c7f257
|
4
|
+
data.tar.gz: 0d87a9ec5d696cbf214ecc99778a5e74694cdd9704a05cebb8370e1892f2c89c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbaa23420bd55bfa12531eae985d01bdec6251737922e079a48fd934c1c7c7343e9fb6d1d85dde978fa7ead83deef69890ef08c5cba40563543f606becc187b7
|
7
|
+
data.tar.gz: 35cbdafb5c311119d9b88f697e669d1a5d99fe3890a9ab6e468f878370e5fb3b745b8df9dc218fb4ef52c2c3e7820e221acef31922b20ee29e13bc1a95d0dba3
|
data/bin/grim
CHANGED
@@ -12,6 +12,7 @@ class GrimCLI < Thor
|
|
12
12
|
class_option :config, type: :string, default: "grim.yml", desc: "Configuration file path"
|
13
13
|
class_option :verbose, type: :boolean, default: false, desc: "Enable verbose output"
|
14
14
|
class_option :json, type: :boolean, default: false, desc: "Output in JSON format"
|
15
|
+
class_option :otp, type: :string, desc: "One-Time Password for secure authentication"
|
15
16
|
|
16
17
|
desc "version", "Show Grim Reaper version"
|
17
18
|
def version
|
@@ -19,9 +20,76 @@ class GrimCLI < Thor
|
|
19
20
|
puts "The Ultimate Backup, Monitoring, and Security System"
|
20
21
|
puts "By Bernie Gengel and his beagle Buddy"
|
21
22
|
end
|
23
|
+
|
24
|
+
desc "otp-setup", "Setup OTP authentication for Grim Reaper"
|
25
|
+
def otp_setup
|
26
|
+
puts "š Setting up OTP authentication...".colorize(:cyan)
|
27
|
+
|
28
|
+
begin
|
29
|
+
grim = GrimReaper::Core.new(load_config)
|
30
|
+
otp_module = grim.get_otp_module
|
31
|
+
|
32
|
+
if otp_module.setup_otp
|
33
|
+
puts "ā
OTP authentication setup complete!".colorize(:green)
|
34
|
+
puts "š± Scan the QR code with your authenticator app".colorize(:blue)
|
35
|
+
puts "š Save your backup codes in a secure location".colorize(:yellow)
|
36
|
+
else
|
37
|
+
puts "ā OTP setup failed".colorize(:red)
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
rescue => e
|
41
|
+
puts "ā OTP setup failed: #{e.message}".colorize(:red)
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "otp-verify CODE", "Verify OTP code"
|
47
|
+
def otp_verify(code)
|
48
|
+
puts "š Verifying OTP code...".colorize(:cyan)
|
49
|
+
|
50
|
+
begin
|
51
|
+
grim = GrimReaper::Core.new(load_config)
|
52
|
+
otp_module = grim.get_otp_module
|
53
|
+
|
54
|
+
if otp_module.verify_otp(code)
|
55
|
+
puts "ā
OTP verification successful!".colorize(:green)
|
56
|
+
else
|
57
|
+
puts "ā Invalid OTP code".colorize(:red)
|
58
|
+
exit 1
|
59
|
+
end
|
60
|
+
rescue => e
|
61
|
+
puts "ā OTP verification failed: #{e.message}".colorize(:red)
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "otp-status", "Check OTP authentication status"
|
67
|
+
def otp_status
|
68
|
+
puts "š Checking OTP status...".colorize(:cyan)
|
69
|
+
|
70
|
+
begin
|
71
|
+
grim = GrimReaper::Core.new(load_config)
|
72
|
+
otp_module = grim.get_otp_module
|
73
|
+
status = otp_module.status
|
74
|
+
|
75
|
+
if options[:json]
|
76
|
+
puts JSON.pretty_generate(status)
|
77
|
+
else
|
78
|
+
puts "OTP Status: #{status[:enabled] ? 'Enabled'.colorize(:green) : 'Disabled'.colorize(:red)}"
|
79
|
+
puts "Last Authentication: #{status[:last_auth] || 'Never'}"
|
80
|
+
puts "Failed Attempts: #{status[:failed_attempts] || 0}"
|
81
|
+
end
|
82
|
+
rescue => e
|
83
|
+
puts "ā Status check failed: #{e.message}".colorize(:red)
|
84
|
+
end
|
85
|
+
end
|
22
86
|
|
23
87
|
desc "setup-complete", "Setup complete Grim Reaper environment (installs Python, Go, Shell, Scythe components)"
|
24
88
|
def setup_complete
|
89
|
+
if options[:otp] && !validate_otp_access
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
25
93
|
puts "š”ļø Setting up complete Grim Reaper environment...".colorize(:cyan)
|
26
94
|
|
27
95
|
begin
|
@@ -36,6 +104,10 @@ class GrimCLI < Thor
|
|
36
104
|
|
37
105
|
desc "health", "Check health of all Grim Reaper modules"
|
38
106
|
def health
|
107
|
+
if options[:otp] && !validate_otp_access
|
108
|
+
return
|
109
|
+
end
|
110
|
+
|
39
111
|
puts "š„ Checking Grim Reaper health...".colorize(:cyan)
|
40
112
|
|
41
113
|
begin
|
@@ -48,19 +120,19 @@ class GrimCLI < Thor
|
|
48
120
|
puts "Grim Reaper Health Status:".colorize(:cyan)
|
49
121
|
all_healthy = true
|
50
122
|
|
51
|
-
status_data.each do |module_name,
|
52
|
-
if
|
53
|
-
puts "
|
54
|
-
else
|
55
|
-
puts " ā #{module_name}: #{data[:status]}".colorize(:red)
|
123
|
+
status_data.each do |module_name, result|
|
124
|
+
if result.is_a?(Hash) && result[:error]
|
125
|
+
puts " #{module_name}: ā #{result[:error]}".colorize(:red)
|
56
126
|
all_healthy = false
|
127
|
+
else
|
128
|
+
puts " #{module_name}: ā
Healthy".colorize(:green)
|
57
129
|
end
|
58
130
|
end
|
59
131
|
|
60
132
|
if all_healthy
|
61
|
-
puts "\nš All modules
|
133
|
+
puts "\nš All modules healthy!".colorize(:green)
|
62
134
|
else
|
63
|
-
puts "\nā ļø Some modules
|
135
|
+
puts "\nā ļø Some modules need attention".colorize(:yellow)
|
64
136
|
end
|
65
137
|
end
|
66
138
|
rescue => e
|
@@ -309,6 +381,11 @@ class GrimCLI < Thor
|
|
309
381
|
puts " install Install dependencies"
|
310
382
|
puts " update Update all modules"
|
311
383
|
puts ""
|
384
|
+
puts "š OTP Security Commands:"
|
385
|
+
puts " otp-setup Setup OTP authentication"
|
386
|
+
puts " otp-verify CODE Verify OTP code"
|
387
|
+
puts " otp-status Check OTP authentication status"
|
388
|
+
puts ""
|
312
389
|
puts "š Ruby-Specific Commands:"
|
313
390
|
puts " rb-setup Setup Ruby environment"
|
314
391
|
puts " rb-analyze PATH Analyze Ruby code quality"
|
@@ -324,29 +401,17 @@ class GrimCLI < Thor
|
|
324
401
|
puts " --config FILE Configuration file path (default: grim.yml)"
|
325
402
|
puts " --verbose Enable verbose output"
|
326
403
|
puts " --json Output in JSON format"
|
404
|
+
puts " --otp CODE One-Time Password for secure authentication"
|
327
405
|
puts ""
|
328
|
-
puts "
|
329
|
-
puts "
|
330
|
-
puts "
|
331
|
-
puts "
|
332
|
-
puts "
|
333
|
-
puts " grim status"
|
334
|
-
puts " grim backup /home/user"
|
335
|
-
puts " grim deploy production --verbose"
|
336
|
-
puts " grim execute 'ls -la' --json"
|
337
|
-
puts ""
|
338
|
-
puts "š Full Grim Reaper CLI Features:"
|
339
|
-
puts " ⢠50+ backup commands (backup-create, backup-verify, etc.)"
|
340
|
-
puts " ⢠30+ monitoring commands (monitor-start, monitor-events, etc.)"
|
341
|
-
puts " ⢠20+ security commands (security-audit, quarantine-isolate, etc.)"
|
342
|
-
puts " ⢠15+ AI/ML commands (ai-analyze, ai-train, ai-predict, etc.)"
|
343
|
-
puts " ⢠10+ optimization commands (optimize-all, heal, etc.)"
|
344
|
-
puts " ⢠License protection system"
|
345
|
-
puts " ⢠Emergency commands"
|
346
|
-
puts " ⢠Web interface and admin server"
|
406
|
+
puts "š Security Features:"
|
407
|
+
puts " ⢠Two-Factor Authentication (TOTP)"
|
408
|
+
puts " ⢠Backup codes for recovery"
|
409
|
+
puts " ⢠Secure command execution"
|
410
|
+
puts " ⢠Authentication tracking"
|
347
411
|
puts ""
|
348
|
-
puts "
|
349
|
-
puts "
|
412
|
+
puts "š Documentation: https://grim.so/docs"
|
413
|
+
puts "š Full Installation: https://get.grim.so"
|
414
|
+
puts "š Gem Page: https://rubygems.org/gems/grim-reaper"
|
350
415
|
end
|
351
416
|
|
352
417
|
private
|
@@ -362,10 +427,81 @@ class GrimCLI < Thor
|
|
362
427
|
{}
|
363
428
|
end
|
364
429
|
end
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
430
|
+
|
431
|
+
private
|
432
|
+
|
433
|
+
def load_config
|
434
|
+
config_file = options[:config]
|
435
|
+
return {} unless File.exist?(config_file)
|
436
|
+
|
437
|
+
case File.extname(config_file)
|
438
|
+
when '.yml', '.yaml'
|
439
|
+
YAML.load_file(config_file) || {}
|
440
|
+
when '.json'
|
441
|
+
JSON.parse(File.read(config_file))
|
442
|
+
else
|
443
|
+
{}
|
444
|
+
end
|
445
|
+
rescue => e
|
446
|
+
warn "Warning: Could not load config file #{config_file}: #{e.message}"
|
447
|
+
{}
|
448
|
+
end
|
449
|
+
|
450
|
+
def validate_otp_access
|
451
|
+
return true unless options[:otp]
|
452
|
+
|
453
|
+
begin
|
454
|
+
grim = GrimReaper::Core.new(load_config)
|
455
|
+
otp_module = grim.get_otp_module
|
456
|
+
|
457
|
+
if otp_module.verify_otp(options[:otp])
|
458
|
+
puts "ā
OTP authentication successful".colorize(:green)
|
459
|
+
return true
|
460
|
+
else
|
461
|
+
puts "ā Invalid OTP code".colorize(:red)
|
462
|
+
exit 1
|
463
|
+
end
|
464
|
+
rescue => e
|
465
|
+
puts "ā OTP verification failed: #{e.message}".colorize(:red)
|
466
|
+
exit 1
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
def execute(command, *args)
|
471
|
+
begin
|
472
|
+
grim = GrimReaper::Core.new(load_config)
|
473
|
+
result = grim.execute(command, *args)
|
474
|
+
|
475
|
+
if options[:json]
|
476
|
+
puts JSON.pretty_generate(result)
|
477
|
+
else
|
478
|
+
result.each do |module_name, data|
|
479
|
+
if data.is_a?(Hash) && data[:error]
|
480
|
+
puts " #{module_name}: ā #{data[:error]}".colorize(:red)
|
481
|
+
else
|
482
|
+
puts " #{module_name}: ā
#{data}".colorize(:green)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
rescue => e
|
487
|
+
puts "ā Command failed: #{e.message}".colorize(:red)
|
488
|
+
exit 1
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def redirect_to_grim_so(command)
|
493
|
+
puts "\nš This command is not available in the Ruby gem.".colorize(:yellow)
|
494
|
+
puts "For the complete Grim Reaper ecosystem with all commands, visit:".colorize(:blue)
|
495
|
+
puts " š https://get.grim.so - Complete installation".colorize(:cyan)
|
496
|
+
puts " š https://grim.so/docs - Documentation".colorize(:cyan)
|
497
|
+
puts " š gem install grim-reaper - Ruby gem".colorize(:cyan)
|
498
|
+
puts "\nThe Ruby gem provides Ruby-specific commands and interfaces to other modules."
|
499
|
+
puts "For command '#{command}', you'll need the full Grim Reaper installation."
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# Start the CLI if this file is executed directly
|
504
|
+
GrimCLI.start(ARGV) if __FILE__ == $0
|
369
505
|
puts "š¦ Or run: curl -sSL https://get.grim.so | bash".colorize(:cyan)
|
370
506
|
puts ""
|
371
507
|
puts "Available commands in this installation:".colorize(:green)
|
data/lib/grim_reaper/core.rb
CHANGED
@@ -62,10 +62,24 @@ module GrimReaper
|
|
62
62
|
@modules[:python] = PythonModule.new(@config, @grim_root)
|
63
63
|
@modules[:go] = GoModule.new(@config, @grim_root)
|
64
64
|
@modules[:security] = SecurityModule.new(@config, @grim_root)
|
65
|
+
@modules[:otp] = OtpModule.new(@config, @grim_root)
|
65
66
|
rescue LoadError => e
|
66
67
|
warn "Warning: Could not load module: #{e.message}"
|
67
68
|
end
|
68
69
|
|
70
|
+
# Get OTP module instance
|
71
|
+
def get_otp_module
|
72
|
+
@modules[:otp] || OtpModule.new(@config, @grim_root)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Validate OTP access if provided
|
76
|
+
def validate_otp_access(otp_code)
|
77
|
+
return true unless otp_code
|
78
|
+
|
79
|
+
otp_module = get_otp_module
|
80
|
+
return otp_module.verify_otp(otp_code)
|
81
|
+
end
|
82
|
+
|
69
83
|
# Execute a command across all modules
|
70
84
|
def execute(command, *args)
|
71
85
|
results = {}
|
@@ -0,0 +1,275 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
require "uri"
|
6
|
+
require "json"
|
7
|
+
require "fileutils"
|
8
|
+
|
9
|
+
module GrimReaper
|
10
|
+
# OTP (One-Time Password) authentication module
|
11
|
+
class OtpModule
|
12
|
+
attr_reader :config, :grim_root, :secret_file, :config_file
|
13
|
+
|
14
|
+
def initialize(config = {}, grim_root = nil)
|
15
|
+
@config = config
|
16
|
+
@grim_root = grim_root || Dir.pwd
|
17
|
+
@secret_file = File.join(@grim_root, ".grim_otp_secret")
|
18
|
+
@config_file = File.join(@grim_root, ".grim_otp_config")
|
19
|
+
end
|
20
|
+
|
21
|
+
# Setup OTP authentication
|
22
|
+
def setup_otp
|
23
|
+
secret = generate_secret
|
24
|
+
|
25
|
+
# Save secret securely
|
26
|
+
File.write(@secret_file, secret)
|
27
|
+
File.chmod(0o600, @secret_file)
|
28
|
+
|
29
|
+
# Generate QR code URL
|
30
|
+
qr_url = generate_qr_url(secret)
|
31
|
+
|
32
|
+
# Generate backup codes
|
33
|
+
backup_codes = generate_backup_codes
|
34
|
+
|
35
|
+
# Save configuration
|
36
|
+
otp_config = {
|
37
|
+
enabled: true,
|
38
|
+
created_at: Time.now.iso8601,
|
39
|
+
backup_codes: backup_codes.map { |code| { code: code, used: false } },
|
40
|
+
failed_attempts: 0,
|
41
|
+
last_auth: nil
|
42
|
+
}
|
43
|
+
|
44
|
+
File.write(@config_file, JSON.pretty_generate(otp_config))
|
45
|
+
File.chmod(0o600, @config_file)
|
46
|
+
|
47
|
+
puts "š OTP Secret: #{secret}".colorize(:green)
|
48
|
+
puts "š± QR Code URL: #{qr_url}".colorize(:blue)
|
49
|
+
puts "š¾ Backup Codes:".colorize(:yellow)
|
50
|
+
backup_codes.each_with_index do |code, index|
|
51
|
+
puts " #{index + 1}. #{code}".colorize(:cyan)
|
52
|
+
end
|
53
|
+
puts "ā ļø Save these backup codes in a secure location!".colorize(:red)
|
54
|
+
|
55
|
+
true
|
56
|
+
rescue => e
|
57
|
+
puts "Error setting up OTP: #{e.message}".colorize(:red)
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
# Verify OTP code
|
62
|
+
def verify_otp(code)
|
63
|
+
return false unless otp_enabled?
|
64
|
+
|
65
|
+
config_data = load_config
|
66
|
+
secret = load_secret
|
67
|
+
|
68
|
+
# Check if it's a backup code
|
69
|
+
if verify_backup_code(code, config_data)
|
70
|
+
mark_backup_code_used(code, config_data)
|
71
|
+
update_last_auth(config_data)
|
72
|
+
return true
|
73
|
+
end
|
74
|
+
|
75
|
+
# Verify TOTP code
|
76
|
+
current_time = Time.now.to_i / 30
|
77
|
+
|
78
|
+
# Check current and previous time windows (to handle clock skew)
|
79
|
+
[-1, 0, 1].each do |offset|
|
80
|
+
if generate_totp(secret, current_time + offset) == code.to_s.rjust(6, '0')
|
81
|
+
update_last_auth(config_data)
|
82
|
+
reset_failed_attempts(config_data)
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Increment failed attempts
|
88
|
+
increment_failed_attempts(config_data)
|
89
|
+
false
|
90
|
+
rescue => e
|
91
|
+
puts "Error verifying OTP: #{e.message}".colorize(:red)
|
92
|
+
false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Check OTP status
|
96
|
+
def status
|
97
|
+
if otp_enabled?
|
98
|
+
config_data = load_config
|
99
|
+
{
|
100
|
+
enabled: true,
|
101
|
+
last_auth: config_data['last_auth'],
|
102
|
+
failed_attempts: config_data['failed_attempts'] || 0,
|
103
|
+
backup_codes_remaining: count_unused_backup_codes(config_data)
|
104
|
+
}
|
105
|
+
else
|
106
|
+
{
|
107
|
+
enabled: false,
|
108
|
+
last_auth: nil,
|
109
|
+
failed_attempts: 0,
|
110
|
+
backup_codes_remaining: 0
|
111
|
+
}
|
112
|
+
end
|
113
|
+
rescue => e
|
114
|
+
{ error: e.message }
|
115
|
+
end
|
116
|
+
|
117
|
+
# Execute OTP-related commands
|
118
|
+
def execute(command, *args)
|
119
|
+
case command
|
120
|
+
when 'setup'
|
121
|
+
setup_otp
|
122
|
+
when 'verify'
|
123
|
+
verify_otp(args.first)
|
124
|
+
when 'status'
|
125
|
+
status
|
126
|
+
when 'disable'
|
127
|
+
disable_otp
|
128
|
+
when 'regenerate-backup-codes'
|
129
|
+
regenerate_backup_codes
|
130
|
+
else
|
131
|
+
{ error: "Unknown OTP command: #{command}" }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Disable OTP authentication
|
136
|
+
def disable_otp
|
137
|
+
File.delete(@secret_file) if File.exist?(@secret_file)
|
138
|
+
File.delete(@config_file) if File.exist?(@config_file)
|
139
|
+
puts "š OTP authentication disabled".colorize(:yellow)
|
140
|
+
true
|
141
|
+
rescue => e
|
142
|
+
puts "Error disabling OTP: #{e.message}".colorize(:red)
|
143
|
+
false
|
144
|
+
end
|
145
|
+
|
146
|
+
# Regenerate backup codes
|
147
|
+
def regenerate_backup_codes
|
148
|
+
return false unless otp_enabled?
|
149
|
+
|
150
|
+
config_data = load_config
|
151
|
+
backup_codes = generate_backup_codes
|
152
|
+
|
153
|
+
config_data['backup_codes'] = backup_codes.map { |code| { code: code, used: false } }
|
154
|
+
|
155
|
+
File.write(@config_file, JSON.pretty_generate(config_data))
|
156
|
+
|
157
|
+
puts "š¾ New Backup Codes:".colorize(:yellow)
|
158
|
+
backup_codes.each_with_index do |code, index|
|
159
|
+
puts " #{index + 1}. #{code}".colorize(:cyan)
|
160
|
+
end
|
161
|
+
|
162
|
+
true
|
163
|
+
rescue => e
|
164
|
+
puts "Error regenerating backup codes: #{e.message}".colorize(:red)
|
165
|
+
false
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
# Check if OTP is enabled
|
171
|
+
def otp_enabled?
|
172
|
+
File.exist?(@secret_file) && File.exist?(@config_file)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Load OTP configuration
|
176
|
+
def load_config
|
177
|
+
return {} unless File.exist?(@config_file)
|
178
|
+
JSON.parse(File.read(@config_file))
|
179
|
+
rescue JSON::ParserError
|
180
|
+
{}
|
181
|
+
end
|
182
|
+
|
183
|
+
# Load OTP secret
|
184
|
+
def load_secret
|
185
|
+
return nil unless File.exist?(@secret_file)
|
186
|
+
File.read(@secret_file).strip
|
187
|
+
end
|
188
|
+
|
189
|
+
# Generate a random secret for TOTP
|
190
|
+
def generate_secret
|
191
|
+
Base64.encode64(OpenSSL::Random.random_bytes(20)).gsub(/\W/, '')[0, 32]
|
192
|
+
end
|
193
|
+
|
194
|
+
# Generate TOTP code
|
195
|
+
def generate_totp(secret, time_counter)
|
196
|
+
# Convert secret from base64
|
197
|
+
key = Base64.decode64(secret)
|
198
|
+
|
199
|
+
# Create HMAC-SHA1
|
200
|
+
time_bytes = [time_counter].pack('Q>')
|
201
|
+
hmac = OpenSSL::HMAC.digest('sha1', key, time_bytes)
|
202
|
+
|
203
|
+
# Dynamic truncation
|
204
|
+
offset = hmac[-1].ord & 0x0f
|
205
|
+
code = hmac[offset, 4].unpack('N').first & 0x7fffffff
|
206
|
+
|
207
|
+
# Return 6-digit code
|
208
|
+
sprintf('%06d', code % 1000000)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Generate QR code URL for authenticator apps
|
212
|
+
def generate_qr_url(secret)
|
213
|
+
account = "grim-reaper@#{Socket.gethostname}"
|
214
|
+
issuer = "Grim Reaper"
|
215
|
+
|
216
|
+
params = {
|
217
|
+
secret: secret,
|
218
|
+
issuer: issuer,
|
219
|
+
algorithm: 'SHA1',
|
220
|
+
digits: 6,
|
221
|
+
period: 30
|
222
|
+
}
|
223
|
+
|
224
|
+
query_string = URI.encode_www_form(params)
|
225
|
+
"otpauth://totp/#{URI.encode_www_form_component(account)}?#{query_string}"
|
226
|
+
end
|
227
|
+
|
228
|
+
# Generate backup codes
|
229
|
+
def generate_backup_codes(count = 10)
|
230
|
+
Array.new(count) do
|
231
|
+
# Generate 8-character alphanumeric codes
|
232
|
+
Array.new(8) { [*'0'..'9', *'A'..'Z'].sample }.join
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Verify backup code
|
237
|
+
def verify_backup_code(code, config_data)
|
238
|
+
backup_codes = config_data['backup_codes'] || []
|
239
|
+
backup_codes.any? { |bc| bc['code'] == code.upcase && !bc['used'] }
|
240
|
+
end
|
241
|
+
|
242
|
+
# Mark backup code as used
|
243
|
+
def mark_backup_code_used(code, config_data)
|
244
|
+
backup_codes = config_data['backup_codes'] || []
|
245
|
+
backup_code = backup_codes.find { |bc| bc['code'] == code.upcase }
|
246
|
+
backup_code['used'] = true if backup_code
|
247
|
+
|
248
|
+
File.write(@config_file, JSON.pretty_generate(config_data))
|
249
|
+
end
|
250
|
+
|
251
|
+
# Count unused backup codes
|
252
|
+
def count_unused_backup_codes(config_data)
|
253
|
+
backup_codes = config_data['backup_codes'] || []
|
254
|
+
backup_codes.count { |bc| !bc['used'] }
|
255
|
+
end
|
256
|
+
|
257
|
+
# Update last authentication time
|
258
|
+
def update_last_auth(config_data)
|
259
|
+
config_data['last_auth'] = Time.now.iso8601
|
260
|
+
File.write(@config_file, JSON.pretty_generate(config_data))
|
261
|
+
end
|
262
|
+
|
263
|
+
# Reset failed attempts counter
|
264
|
+
def reset_failed_attempts(config_data)
|
265
|
+
config_data['failed_attempts'] = 0
|
266
|
+
File.write(@config_file, JSON.pretty_generate(config_data))
|
267
|
+
end
|
268
|
+
|
269
|
+
# Increment failed attempts counter
|
270
|
+
def increment_failed_attempts(config_data)
|
271
|
+
config_data['failed_attempts'] = (config_data['failed_attempts'] || 0) + 1
|
272
|
+
File.write(@config_file, JSON.pretty_generate(config_data))
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
data/lib/grim_reaper/version.rb
CHANGED
data/lib/grim_reaper.rb
CHANGED
@@ -6,6 +6,7 @@ require_relative "grim_reaper/shell_module"
|
|
6
6
|
require_relative "grim_reaper/python_module"
|
7
7
|
require_relative "grim_reaper/go_module"
|
8
8
|
require_relative "grim_reaper/security_module"
|
9
|
+
require_relative "grim_reaper/otp_module"
|
9
10
|
require_relative "grim_reaper/installer"
|
10
11
|
|
11
12
|
# Grim Reaper - The Ultimate Backup, Monitoring, and Security System
|
@@ -38,4 +39,12 @@ module GrimReaper
|
|
38
39
|
def self.security
|
39
40
|
Core.new.execute('security')
|
40
41
|
end
|
42
|
+
|
43
|
+
def self.setup_otp
|
44
|
+
Core.new.get_otp_module.setup_otp
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.verify_otp(code)
|
48
|
+
Core.new.get_otp_module.verify_otp(code)
|
49
|
+
end
|
41
50
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grim-reaper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.33
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernie Gengel and his beagle Buddy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-07-
|
11
|
+
date: 2025-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -181,8 +181,8 @@ dependencies:
|
|
181
181
|
description: When data death comes knocking, Grim ensures resurrection is just a command
|
182
182
|
away. License management, auto backups, highly compressed backups, multi-algorithm
|
183
183
|
compression, content-based deduplication, smart storage tiering save up to 60% space,
|
184
|
-
military-grade encryption, license protection, security surveillance,
|
185
|
-
threat response.
|
184
|
+
military-grade encryption, license protection, security surveillance, OTP authentication,
|
185
|
+
and automated threat response.
|
186
186
|
email:
|
187
187
|
- rip@grim.so
|
188
188
|
executables:
|
@@ -206,6 +206,7 @@ files:
|
|
206
206
|
- lib/grim_reaper/core.rb
|
207
207
|
- lib/grim_reaper/go_module.rb
|
208
208
|
- lib/grim_reaper/installer.rb
|
209
|
+
- lib/grim_reaper/otp_module.rb
|
209
210
|
- lib/grim_reaper/python_module.rb
|
210
211
|
- lib/grim_reaper/security_module.rb
|
211
212
|
- lib/grim_reaper/shell_module.rb
|
@@ -219,13 +220,17 @@ metadata:
|
|
219
220
|
source_code_uri: https://github.com/cyber-boost/grim
|
220
221
|
changelog_uri: https://github.com/cyber-boost/grim/blob/main/CHANGELOG.md
|
221
222
|
rubygems_mfa_required: 'true'
|
222
|
-
post_install_message: "\U0001F5E1ļø Grim Reaper Ruby Gem v1.0.
|
223
|
-
|
224
|
-
|
225
|
-
components (
|
226
|
-
|
227
|
-
|
228
|
-
|
223
|
+
post_install_message: "\U0001F5E1ļø Grim Reaper Ruby Gem v1.0.33 installed successfully!\n\n\U0001F195
|
224
|
+
NEW: OTP (One-Time Password) Authentication Support!\nRun 'grim otp-setup' to enable
|
225
|
+
secure two-factor authentication.\n\nThe gem provides a unified interface to all
|
226
|
+
Grim Reaper modules:\n- Python components (scythe, py_grim)\n- Go components (go_grim)\n-
|
227
|
+
Shell components (sh_grim)\n- Ruby components (rb_grim)\n- OTP Security Module (NEW!)\n\nRun
|
228
|
+
'grim rb-setup' to configure your environment and install all components.\nRun 'grim
|
229
|
+
help' to see all available commands.\n\nFor automatic installation of all Grim Reaper
|
230
|
+
components, run:\ngrim setup-complete\n\n\U0001F510 Security Enhancement:\n⢠Use
|
231
|
+
--otp flag for secure command execution\n⢠Setup OTP: grim otp-setup\n⢠Verify OTP:
|
232
|
+
grim otp-verify <code>\n⢠Check status: grim otp-status\n\n\U0001F4A1 For missing
|
233
|
+
commands, visit: https://get.grim.so\n"
|
229
234
|
rdoc_options: []
|
230
235
|
require_paths:
|
231
236
|
- lib
|
@@ -243,5 +248,5 @@ requirements: []
|
|
243
248
|
rubygems_version: 3.4.20
|
244
249
|
signing_key:
|
245
250
|
specification_version: 4
|
246
|
-
summary: 'Grim: Unified Data Protection Ecosystem'
|
251
|
+
summary: 'Grim: Unified Data Protection Ecosystem with OTP Security'
|
247
252
|
test_files: []
|