console1984 0.1.7 → 0.1.11
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 +8 -6
- data/config/protections.yml +31 -0
- data/lib/console1984/command_executor.rb +14 -6
- data/lib/console1984/command_validator/forbidden_constant_reference_validation.rb +3 -3
- data/lib/console1984/command_validator/forbidden_reopening_validation.rb +3 -3
- data/lib/console1984/command_validator/parsed_command.rb +3 -43
- data/lib/console1984/command_validator/suspicious_terms_validation.rb +1 -1
- data/lib/console1984/command_validator.rb +1 -1
- data/lib/console1984/config.rb +9 -1
- data/lib/console1984/engine.rb +1 -1
- data/lib/console1984/errors.rb +6 -2
- data/lib/console1984/ext/active_record/protected_auditable_tables.rb +2 -2
- data/lib/console1984/ext/core/module.rb +32 -0
- data/lib/console1984/ext/core/object.rb +2 -1
- data/lib/console1984/ext/socket/tcp_socket.rb +5 -1
- data/lib/console1984/freezeable.rb +14 -8
- data/lib/console1984/protections_config.rb +17 -0
- data/lib/console1984/refrigerator.rb +31 -0
- data/lib/console1984/shield/method_invocation_shell.rb +46 -0
- data/lib/console1984/shield.rb +8 -9
- data/lib/console1984/supervisor.rb +9 -0
- data/lib/console1984/version.rb +1 -1
- metadata +7 -3
- data/config/command_protections.yml +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 472b88a03c5befd81877d190baa41cd0cd4d4a985fff797ddc447e729e6fa56a
|
4
|
+
data.tar.gz: 9c791955b0caf6d49fd655905a9d37a231bb7ee668f5b2a1dd85e4ebd14efdaa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8300ef44660e9b9933ae68ebfc4b26f0e57849c716b1339817e00332407a0696df4e46599f056be186171cc884bf8c8fb4c1eba688aecf7702c04d1ad0ad69a9
|
7
|
+
data.tar.gz: b7a073d5bfc4f648fc10f5bb331320ae0b84a7afa3e1108df9005faedc2f51a338fd9b3d659ecf5fe0744bee6067e444e75dd67a12c53647d6c079fdb554e5fa
|
data/README.md
CHANGED
@@ -106,11 +106,7 @@ irb(main)> Topic.last.name
|
|
106
106
|
=> "{\"p\":\"iu6+LfnNlurC6sL++JyOIDvedjNSz/AvnZQ=\",\"h\":{\"iv\":\"BYa86+JNM/LdkC18\",\"at\":\"r4sQNoSyIlAjJdZEKHVMow==\",\"k\":{\"p\":\"7L1l/5UiYsFQqqo4jfMZtLwp90KqcrIgS7HqgteVjuM=\",\"h\":{\"iv\":\"ItwRYxZAerKIoSZ8\",\"at\":\"ZUSNVfvtm4wAYWLBKRAx/g==\",\"e\":\"QVNDSUktOEJJVA==\"}},\"i\":\"OTdiOQ==\"}}"
|
107
107
|
```
|
108
108
|
|
109
|
-
While in protected mode, you can't modify encrypted data, but can save unencrypted attributes normally. If you try to modify an encrypted column it will raise an error
|
110
|
-
|
111
|
-
```ruby
|
112
|
-
irb(main)> Rails.cache.read("some key") # raises Console1984::Errors::ProtectedConnection
|
113
|
-
```
|
109
|
+
While in protected mode, you can't modify encrypted data, but can save unencrypted attributes normally. If you try to modify an encrypted column it will raise an error.
|
114
110
|
|
115
111
|
### Access to external systems
|
116
112
|
|
@@ -122,7 +118,13 @@ To protect the access to such systems, you can add their URLs to `config.console
|
|
122
118
|
config.console1984.protected_urls = [ "https://my-app-us-east-1-whatever.us-east-1.es.amazonaws.com", "redis://my-app-cache-1.whatever.cache.amazonaws.com:6379" ]
|
123
119
|
```
|
124
120
|
|
125
|
-
|
121
|
+
In the default protected mode, trying to read data from a protected system will be aborted with an error:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
irb(main)> Rails.cache.read("some key") # raises Console1984::Errors::ProtectedConnection
|
125
|
+
```
|
126
|
+
|
127
|
+
Running `decrypt!` will switch you to unprotected mode and let you access these systems normally. The system will ask for a justfication and will flag those accesses as sensitive.
|
126
128
|
|
127
129
|
This will work for systems that use Ruby sockets as the underlying communication mechanism.
|
128
130
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
validations:
|
2
|
+
forbidden_reopening:
|
3
|
+
- ActiveRecord
|
4
|
+
- Console1984
|
5
|
+
- PG
|
6
|
+
- Mysql2
|
7
|
+
- IRB
|
8
|
+
forbidden_constant_reference:
|
9
|
+
always:
|
10
|
+
- Console1984
|
11
|
+
- IRB
|
12
|
+
protected:
|
13
|
+
- PG
|
14
|
+
- Mysql2
|
15
|
+
- ActiveRecord::ActiveRecordEncryption
|
16
|
+
suspicious_terms:
|
17
|
+
- console_1984
|
18
|
+
- Console1984
|
19
|
+
- secret
|
20
|
+
- credentials
|
21
|
+
- irb
|
22
|
+
forbidden_methods:
|
23
|
+
Kernel:
|
24
|
+
- eval
|
25
|
+
Object:
|
26
|
+
- eval
|
27
|
+
BasicObject:
|
28
|
+
- eval
|
29
|
+
- instance_eval
|
30
|
+
Module:
|
31
|
+
- class_eval
|
@@ -19,13 +19,15 @@ class Console1984::CommandExecutor
|
|
19
19
|
run_as_system { session_logger.before_executing commands }
|
20
20
|
validate_command commands
|
21
21
|
execute_in_protected_mode(&block)
|
22
|
-
rescue Console1984::Errors::
|
22
|
+
rescue Console1984::Errors::ForbiddenCommandAttempted, FrozenError
|
23
23
|
flag_suspicious(commands)
|
24
|
-
rescue Console1984::Errors::
|
24
|
+
rescue Console1984::Errors::SuspiciousCommandAttempted
|
25
25
|
flag_suspicious(commands)
|
26
26
|
execute_in_protected_mode(&block)
|
27
|
-
rescue
|
27
|
+
rescue Console1984::Errors::ForbiddenCommandExecuted
|
28
|
+
# We detected that a forbidden command was executed. We exit IRB right away.
|
28
29
|
flag_suspicious(commands)
|
30
|
+
Console1984.supervisor.exit_irb
|
29
31
|
ensure
|
30
32
|
run_as_system { session_logger.after_executing commands }
|
31
33
|
end
|
@@ -67,15 +69,21 @@ class Console1984::CommandExecutor
|
|
67
69
|
command_validator.validate(command)
|
68
70
|
end
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
+
def from_irb?(backtrace)
|
73
|
+
executing_user_command? && backtrace.find do |line|
|
74
|
+
line_from_irb = line =~ /^[^\/]/
|
75
|
+
break if !(line =~ /console1984\/lib/ || line_from_irb)
|
76
|
+
line_from_irb
|
77
|
+
end
|
78
|
+
end
|
72
79
|
|
80
|
+
private
|
73
81
|
def command_validator
|
74
82
|
@command_validator ||= build_command_validator
|
75
83
|
end
|
76
84
|
|
77
85
|
def build_command_validator
|
78
|
-
Console1984::CommandValidator.from_config(
|
86
|
+
Console1984::CommandValidator.from_config(Console1984.protections_config.validations)
|
79
87
|
end
|
80
88
|
|
81
89
|
def flag_suspicious(commands)
|
@@ -14,17 +14,17 @@ class Console1984::CommandValidator::ForbiddenConstantReferenceValidation
|
|
14
14
|
@constant_names_forbidden_in_protected_mode = config[:protected] || []
|
15
15
|
end
|
16
16
|
|
17
|
-
# Raises a Console1984::Errors::
|
17
|
+
# Raises a Console1984::Errors::ForbiddenCommandAttempted if a banned constant is referenced.
|
18
18
|
def validate(parsed_command)
|
19
19
|
if contains_invalid_const_reference?(parsed_command, @forbidden_constants_names) ||
|
20
20
|
(@shield.protected_mode? && contains_invalid_const_reference?(parsed_command, @constant_names_forbidden_in_protected_mode))
|
21
|
-
raise Console1984::Errors::
|
21
|
+
raise Console1984::Errors::ForbiddenCommandAttempted
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
26
26
|
def contains_invalid_const_reference?(parsed_command, banned_constants)
|
27
|
-
parsed_command.constants.find do |constant_name|
|
27
|
+
(parsed_command.constants + parsed_command.constant_assignments).find do |constant_name|
|
28
28
|
banned_constants.find { |banned_constant| "#{constant_name}::".start_with?("#{banned_constant}::") }
|
29
29
|
end
|
30
30
|
end
|
@@ -8,17 +8,17 @@ class Console1984::CommandValidator::ForbiddenReopeningValidation
|
|
8
8
|
@banned_class_or_module_names = banned_classes_or_modules.collect(&:to_s)
|
9
9
|
end
|
10
10
|
|
11
|
-
# Raises a Console1984::Errors::
|
11
|
+
# Raises a Console1984::Errors::ForbiddenCommandAttempted if an banned class or module reopening
|
12
12
|
# is detected.
|
13
13
|
def validate(parsed_command)
|
14
14
|
if contains_invalid_class_or_module_declaration?(parsed_command)
|
15
|
-
raise Console1984::Errors::
|
15
|
+
raise Console1984::Errors::ForbiddenCommandAttempted
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
20
20
|
def contains_invalid_class_or_module_declaration?(parsed_command)
|
21
|
-
parsed_command.declared_classes_or_modules.find { |class_or_module_name| banned?(class_or_module_name) }
|
21
|
+
(parsed_command.declared_classes_or_modules + parsed_command.constant_assignments).find { |class_or_module_name| banned?(class_or_module_name) }
|
22
22
|
end
|
23
23
|
|
24
24
|
def banned?(class_or_module_name)
|
@@ -6,59 +6,19 @@ class Console1984::CommandValidator::ParsedCommand
|
|
6
6
|
|
7
7
|
attr_reader :raw_command
|
8
8
|
|
9
|
-
delegate :declared_classes_or_modules, :constants, to: :
|
9
|
+
delegate :declared_classes_or_modules, :constants, :constant_assignments, to: :command_parser
|
10
10
|
|
11
11
|
def initialize(raw_command)
|
12
12
|
@raw_command = Array(raw_command).join("\n")
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
16
|
-
def
|
17
|
-
@
|
16
|
+
def command_parser
|
17
|
+
@command_parser ||= Console1984::CommandValidator::CommandParser.new.tap do |processor|
|
18
18
|
ast = Parser::CurrentRuby.parse(raw_command)
|
19
19
|
processor.process(ast)
|
20
20
|
rescue Parser::SyntaxError
|
21
21
|
# Fail open with syntax errors
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
|
-
class CommandProcessor < ::Parser::AST::Processor
|
26
|
-
include AST::Processor::Mixin
|
27
|
-
include Console1984::Freezeable
|
28
|
-
|
29
|
-
attr_reader :constants, :declared_classes_or_modules
|
30
|
-
|
31
|
-
def initialize
|
32
|
-
@constants = []
|
33
|
-
@declared_classes_or_modules = []
|
34
|
-
end
|
35
|
-
|
36
|
-
def on_class(node)
|
37
|
-
super
|
38
|
-
const_declaration, _, _ = *node
|
39
|
-
|
40
|
-
processor = self.class.new
|
41
|
-
processor.process(const_declaration)
|
42
|
-
@declared_classes_or_modules << processor.constants.first if processor.constants.present?
|
43
|
-
end
|
44
|
-
|
45
|
-
alias_method :on_module, :on_class
|
46
|
-
|
47
|
-
def on_const(node)
|
48
|
-
super
|
49
|
-
name, const_name = *node
|
50
|
-
const_name = const_name.to_s
|
51
|
-
last_constant = @constants.last
|
52
|
-
|
53
|
-
if name.nil? || (name && name.type == :cbase) # cbase = leading ::
|
54
|
-
if last_constant&.end_with?("::")
|
55
|
-
last_constant << const_name
|
56
|
-
else
|
57
|
-
@constants << const_name
|
58
|
-
end
|
59
|
-
elsif last_constant
|
60
|
-
last_constant << "::#{const_name}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
24
|
end
|
@@ -9,7 +9,7 @@ class Console1984::CommandValidator::SuspiciousTermsValidation
|
|
9
9
|
# Raises a Console1984::Errors::SuspiciousCommand if the term is referenced.
|
10
10
|
def validate(parsed_command)
|
11
11
|
if contains_suspicious_term?(parsed_command)
|
12
|
-
raise Console1984::Errors::
|
12
|
+
raise Console1984::Errors::SuspiciousCommandAttempted
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
# The validation itself happens as a chain of validation objects. The system will invoke
|
7
7
|
# each validation in order. Validations will raise an error if the validation fails (typically
|
8
|
-
# a Console1984::Errors::
|
8
|
+
# a Console1984::Errors::ForbiddenCommandAttempted or Console1984::Errors::SuspiciousCommands).
|
9
9
|
#
|
10
10
|
# Internally, validations will receive a Console1984::CommandValidator::ParsedCommand object. This
|
11
11
|
# exposes parsed constructs in addition to the raw strings so that validations can use those.
|
data/lib/console1984/config.rb
CHANGED
@@ -4,11 +4,14 @@
|
|
4
4
|
class Console1984::Config
|
5
5
|
include Console1984::Freezeable, Console1984::Messages
|
6
6
|
|
7
|
+
PROTECTIONS_CONFIG_FILE_PATH = Console1984::Engine.root.join("config/protections.yml")
|
8
|
+
|
7
9
|
PROPERTIES = %i[
|
8
10
|
session_logger username_resolver shield command_executor
|
9
11
|
protected_environments protected_urls
|
10
12
|
production_data_warning enter_unprotected_encryption_mode_warning enter_protected_mode_warning
|
11
13
|
incinerate incinerate_after incineration_queue
|
14
|
+
protections_config
|
12
15
|
debug test_mode
|
13
16
|
]
|
14
17
|
|
@@ -24,9 +27,14 @@ class Console1984::Config
|
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
30
|
+
# Initialize lazily so that it only gets instantiated during console sessions
|
31
|
+
def protections_config
|
32
|
+
@protections_config ||= Console1984::ProtectionsConfig.new(YAML.safe_load(File.read(PROTECTIONS_CONFIG_FILE_PATH)).symbolize_keys)
|
33
|
+
end
|
34
|
+
|
27
35
|
def freeze
|
28
36
|
super
|
29
|
-
[ protected_urls ].each(&:freeze)
|
37
|
+
[ protected_urls, protections_config ].each(&:freeze)
|
30
38
|
end
|
31
39
|
|
32
40
|
private
|
data/lib/console1984/engine.rb
CHANGED
@@ -10,7 +10,7 @@ module Console1984
|
|
10
10
|
|
11
11
|
initializer "console1984.config" do
|
12
12
|
config.console1984.each do |key, value|
|
13
|
-
Console1984.config.send("#{key}=", value)
|
13
|
+
Console1984.config.send("#{key}=", value)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
data/lib/console1984/errors.rb
CHANGED
@@ -10,11 +10,15 @@ module Console1984
|
|
10
10
|
|
11
11
|
# Attempt to execute a command that is not allowed. The system won't
|
12
12
|
# execute such commands and will flag them as sensitive.
|
13
|
-
class
|
13
|
+
class ForbiddenCommandAttempted < StandardError; end
|
14
14
|
|
15
15
|
# A suspicious command was executed. The command will be flagged but the system
|
16
16
|
# will let it run.
|
17
|
-
class
|
17
|
+
class SuspiciousCommandAttempted < StandardError; end
|
18
|
+
|
19
|
+
# A forbidden command was executed. The system will flag the command
|
20
|
+
# and exit.
|
21
|
+
class ForbiddenCommandExecuted < StandardError; end
|
18
22
|
|
19
23
|
# Attempt to incinerate a session ahead of time as determined by
|
20
24
|
# +config.console1984.incinerate_after+.
|
@@ -6,7 +6,7 @@ module Console1984::Ext::ActiveRecord::ProtectedAuditableTables
|
|
6
6
|
define_method method do |*args, **kwargs|
|
7
7
|
sql = args.first
|
8
8
|
if Console1984.command_executor.executing_user_command? && sql =~ auditable_tables_regexp
|
9
|
-
raise Console1984::Errors::
|
9
|
+
raise Console1984::Errors::ForbiddenCommandAttempted, "#{sql}"
|
10
10
|
else
|
11
11
|
super(*args, **kwargs)
|
12
12
|
end
|
@@ -19,7 +19,7 @@ module Console1984::Ext::ActiveRecord::ProtectedAuditableTables
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def auditable_tables
|
22
|
-
@auditable_tables ||= auditable_models.collect(&:table_name)
|
22
|
+
@auditable_tables ||= Console1984.command_executor.run_as_system { auditable_models.collect(&:table_name) }
|
23
23
|
end
|
24
24
|
|
25
25
|
def auditable_models
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Extends +Module+ to prevent invoking class_eval in user commands.
|
2
|
+
#
|
3
|
+
# We don't use the built-in configurable system from protections.yml because we use
|
4
|
+
# class_eval ourselves to implement it!
|
5
|
+
module Console1984::Ext::Core::Module
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def instance_eval(*)
|
9
|
+
if Console1984.command_executor.executing_user_command?
|
10
|
+
raise Console1984::Errors::ForbiddenCommandAttempted
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_added(method)
|
17
|
+
if Console1984.command_executor.from_irb?(caller) && banned_for_reopening?
|
18
|
+
raise Console1984::Errors::ForbiddenCommandExecuted, "Trying to add method `#{method}` to #{self.name}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def banned_for_reopening?
|
24
|
+
classes_and_modules_banned_for_reopening.find do |banned_class_or_module_name|
|
25
|
+
"#{self.name}::".start_with?("#{banned_class_or_module_name}::")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def classes_and_modules_banned_for_reopening
|
30
|
+
@classes_and_modules_banned_for_reopening ||= Console1984.protections_config.validations[:forbidden_reopening]
|
31
|
+
end
|
32
|
+
end
|
@@ -12,6 +12,7 @@ module Console1984::Ext::Core::Object
|
|
12
12
|
extend ActiveSupport::Concern
|
13
13
|
|
14
14
|
include Console1984::Freezeable
|
15
|
+
self.prevent_instance_data_manipulation_after_freezing = false
|
15
16
|
|
16
17
|
class_methods do
|
17
18
|
def const_get(*arguments)
|
@@ -24,7 +25,7 @@ module Console1984::Ext::Core::Object
|
|
24
25
|
# See the list +forbidden_reopening+ in +config/command_protections.yml+.
|
25
26
|
Console1984.command_executor.validate_command("class #{arguments.first}; end")
|
26
27
|
super
|
27
|
-
rescue Console1984::Errors::
|
28
|
+
rescue Console1984::Errors::ForbiddenCommandAttempted
|
28
29
|
raise
|
29
30
|
rescue StandardError
|
30
31
|
super
|
@@ -28,12 +28,16 @@ module Console1984::Ext::Socket::TcpSocket
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def protected_addresses
|
31
|
-
@protected_addresses ||=
|
31
|
+
@protected_addresses ||= protected_urls.collect do |url|
|
32
32
|
host, port = host_and_port_from(url)
|
33
33
|
Array(Addrinfo.getaddrinfo(host, port)).collect { |addrinfo| ComparableAddress.new(addrinfo) if addrinfo.ip_address }
|
34
34
|
end.flatten.compact.uniq
|
35
35
|
end
|
36
36
|
|
37
|
+
def protected_urls
|
38
|
+
Console1984::Shield::Modes::PROTECTED_MODE.currently_protected_urls || []
|
39
|
+
end
|
40
|
+
|
37
41
|
def host_and_port_from(url)
|
38
42
|
URI(url).then do |parsed_uri|
|
39
43
|
if parsed_uri.host
|
@@ -13,18 +13,24 @@
|
|
13
13
|
# will look through all the modules/classes freezing them. This way, we can control
|
14
14
|
# the moment where we stop classes from being modifiable at setup time.
|
15
15
|
module Console1984::Freezeable
|
16
|
-
extend ActiveSupport::Concern
|
17
|
-
|
18
16
|
mattr_reader :to_freeze, default: Set.new
|
19
17
|
|
20
|
-
|
21
|
-
|
18
|
+
# Not using ActiveSupport::Concern because a bunch of classes skip its +.invoked+ hook which
|
19
|
+
# is terrible for our purposes. This happened because it was being included in parent classes
|
20
|
+
# (such as Object), so it was skipping the include block.
|
21
|
+
def self.included(base)
|
22
|
+
Console1984::Freezeable.to_freeze << base
|
23
|
+
base.extend ClassMethods
|
24
|
+
|
25
|
+
# Flag to control manipulating instance data via instance_variable_get and instance_variable_set.
|
26
|
+
# true by default.
|
27
|
+
base.thread_mattr_accessor :prevent_instance_data_manipulation_after_freezing, default: true
|
22
28
|
end
|
23
29
|
|
24
|
-
|
30
|
+
module ClassMethods
|
25
31
|
SENSITIVE_INSTANCE_METHODS = %i[ instance_variable_get instance_variable_set ]
|
26
32
|
|
27
|
-
def
|
33
|
+
def prevent_instance_data_manipulation
|
28
34
|
SENSITIVE_INSTANCE_METHODS.each do |method|
|
29
35
|
prevent_sensitive_method method
|
30
36
|
end
|
@@ -33,7 +39,7 @@ module Console1984::Freezeable
|
|
33
39
|
private
|
34
40
|
def prevent_sensitive_method(method_name)
|
35
41
|
define_method method_name do |*arguments|
|
36
|
-
raise Console1984::Errors::
|
42
|
+
raise Console1984::Errors::ForbiddenCommandAttempted, "You can't invoke #{method_name} on #{self}"
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
@@ -51,7 +57,7 @@ module Console1984::Freezeable
|
|
51
57
|
end
|
52
58
|
|
53
59
|
def freeze_class_or_module(class_or_module)
|
54
|
-
class_or_module.
|
60
|
+
class_or_module.prevent_instance_data_manipulation if class_or_module.prevent_instance_data_manipulation_after_freezing
|
55
61
|
class_or_module.freeze
|
56
62
|
end
|
57
63
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Console1984::ProtectionsConfig
|
2
|
+
include Console1984::Freezeable
|
3
|
+
|
4
|
+
delegate :validations, to: :instance
|
5
|
+
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
%i[ validations forbidden_methods ].each do |method_name|
|
13
|
+
define_method method_name do
|
14
|
+
config[method_name].symbolize_keys
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Freezes classes to prevent tampering them
|
2
|
+
class Console1984::Refrigerator
|
3
|
+
include Console1984::Freezeable
|
4
|
+
|
5
|
+
def freeze_all
|
6
|
+
eager_load_all_classes
|
7
|
+
freeze_internal_instances # internal modules and classes are frozen by including Console1984::Freezable
|
8
|
+
freeze_external_modules_and_classes
|
9
|
+
|
10
|
+
Console1984::Freezeable.freeze_all
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def freeze_internal_instances
|
15
|
+
Console1984.config.freeze unless Console1984.config.test_mode
|
16
|
+
end
|
17
|
+
|
18
|
+
def freeze_external_modules_and_classes
|
19
|
+
external_modules_and_classes_to_freeze.each { |klass| klass.include(Console1984::Freezeable) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def external_modules_and_classes_to_freeze
|
23
|
+
# Not using a constant because we want this to run lazily (console-dependant dependencies might not be loaded).
|
24
|
+
[Parser::CurrentRuby]
|
25
|
+
end
|
26
|
+
|
27
|
+
def eager_load_all_classes
|
28
|
+
Rails.application.eager_load! unless Rails.application.config.eager_load
|
29
|
+
Console1984.class_loader.eager_load
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Prevents invoking a configurable set of methods
|
2
|
+
class Console1984::Shield::MethodInvocationShell
|
3
|
+
include Console1984::Freezeable
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def install_for(invocations)
|
7
|
+
Array(invocations).each { |invocation| self.new(invocation).prevent_methods_invocation }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :class_name, :methods, :only_for_user_commands
|
12
|
+
|
13
|
+
def initialize(invocation)
|
14
|
+
@class_name, methods = invocation.to_a
|
15
|
+
@methods = Array(methods)
|
16
|
+
end
|
17
|
+
|
18
|
+
def prevent_methods_invocation
|
19
|
+
class_name.to_s.constantize.prepend build_protection_module
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_protection_module
|
23
|
+
source = protected_method_invocations_source
|
24
|
+
Module.new do
|
25
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
26
|
+
#{source}
|
27
|
+
RUBY
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def protected_method_invocations_source
|
32
|
+
methods.collect { |method| protected_method_invocation_source_for(method) }.join("\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
def protected_method_invocation_source_for(method)
|
36
|
+
<<~RUBY
|
37
|
+
def #{method}(*args)
|
38
|
+
if Console1984.command_executor.from_irb?(caller)
|
39
|
+
raise Console1984::Errors::ForbiddenCommandAttempted
|
40
|
+
else
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
end
|
46
|
+
end
|
data/lib/console1984/shield.rb
CHANGED
@@ -19,7 +19,9 @@ class Console1984::Shield
|
|
19
19
|
# that aren't mean to be modified once the console is running.
|
20
20
|
def install
|
21
21
|
extend_protected_systems
|
22
|
-
|
22
|
+
prevent_invoking_protected_methods
|
23
|
+
|
24
|
+
refrigerator.freeze_all
|
23
25
|
end
|
24
26
|
|
25
27
|
private
|
@@ -37,6 +39,7 @@ class Console1984::Shield
|
|
37
39
|
|
38
40
|
def extend_core_ruby
|
39
41
|
Object.prepend Console1984::Ext::Core::Object
|
42
|
+
Module.prepend Console1984::Ext::Core::Module
|
40
43
|
end
|
41
44
|
|
42
45
|
def extend_sockets
|
@@ -65,16 +68,12 @@ class Console1984::Shield
|
|
65
68
|
end
|
66
69
|
end
|
67
70
|
|
68
|
-
def
|
69
|
-
|
70
|
-
Console1984.config.freeze unless Console1984.config.test_mode
|
71
|
-
Console1984::Freezeable.freeze_all
|
72
|
-
Parser::CurrentRuby.freeze
|
71
|
+
def prevent_invoking_protected_methods
|
72
|
+
MethodInvocationShell.install_for(Console1984.protections_config.forbidden_methods)
|
73
73
|
end
|
74
74
|
|
75
|
-
def
|
76
|
-
|
77
|
-
Console1984.class_loader.eager_load
|
75
|
+
def refrigerator
|
76
|
+
@refrigerator ||= Console1984::Refrigerator.new
|
78
77
|
end
|
79
78
|
|
80
79
|
module SSLSocketRemoteAddress
|
@@ -30,12 +30,21 @@ class Console1984::Supervisor
|
|
30
30
|
stop_session
|
31
31
|
end
|
32
32
|
|
33
|
+
def exit_irb
|
34
|
+
stop
|
35
|
+
IRB.CurrentContext.exit
|
36
|
+
end
|
37
|
+
|
33
38
|
private
|
34
39
|
def require_dependencies
|
35
40
|
Kernel.silence_warnings do
|
36
41
|
require 'parser/current'
|
37
42
|
end
|
38
43
|
require 'colorized_string'
|
44
|
+
|
45
|
+
# Explicit lazy loading because it depends on +parser+, which we want to only load
|
46
|
+
# in console sessions.
|
47
|
+
require_relative "./command_validator/.command_parser"
|
39
48
|
end
|
40
49
|
|
41
50
|
def start_session
|
data/lib/console1984/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: console1984
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jorge Manrubia
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -195,7 +195,7 @@ files:
|
|
195
195
|
- app/models/console1984/session.rb
|
196
196
|
- app/models/console1984/session/incineratable.rb
|
197
197
|
- app/models/console1984/user.rb
|
198
|
-
- config/
|
198
|
+
- config/protections.yml
|
199
199
|
- db/migrate/20210517203931_create_console1984_tables.rb
|
200
200
|
- lib/console1984.rb
|
201
201
|
- lib/console1984/command_executor.rb
|
@@ -208,6 +208,7 @@ files:
|
|
208
208
|
- lib/console1984/engine.rb
|
209
209
|
- lib/console1984/errors.rb
|
210
210
|
- lib/console1984/ext/active_record/protected_auditable_tables.rb
|
211
|
+
- lib/console1984/ext/core/module.rb
|
211
212
|
- lib/console1984/ext/core/object.rb
|
212
213
|
- lib/console1984/ext/irb/commands.rb
|
213
214
|
- lib/console1984/ext/irb/context.rb
|
@@ -215,8 +216,11 @@ files:
|
|
215
216
|
- lib/console1984/freezeable.rb
|
216
217
|
- lib/console1984/input_output.rb
|
217
218
|
- lib/console1984/messages.rb
|
219
|
+
- lib/console1984/protections_config.rb
|
220
|
+
- lib/console1984/refrigerator.rb
|
218
221
|
- lib/console1984/sessions_logger/database.rb
|
219
222
|
- lib/console1984/shield.rb
|
223
|
+
- lib/console1984/shield/method_invocation_shell.rb
|
220
224
|
- lib/console1984/shield/modes.rb
|
221
225
|
- lib/console1984/shield/modes/protected.rb
|
222
226
|
- lib/console1984/shield/modes/unprotected.rb
|
@@ -1,17 +0,0 @@
|
|
1
|
-
forbidden_reopening:
|
2
|
-
- ActiveRecord
|
3
|
-
- Console1984
|
4
|
-
- PG
|
5
|
-
- Mysql2
|
6
|
-
forbidden_constant_reference:
|
7
|
-
always:
|
8
|
-
- Console1984
|
9
|
-
protected:
|
10
|
-
- PG
|
11
|
-
- Mysql2
|
12
|
-
- ActiveRecord::ActiveRecordEncryption
|
13
|
-
suspicious_terms:
|
14
|
-
- console_1984
|
15
|
-
- Console1984
|
16
|
-
- secret
|
17
|
-
- credentials
|