console1984 0.1.7 → 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|