console1984 0.1.6 → 0.1.7

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -1
  3. data/config/command_protections.yml +17 -0
  4. data/lib/console1984/command_executor.rb +94 -0
  5. data/lib/console1984/command_validator/forbidden_constant_reference_validation.rb +31 -0
  6. data/lib/console1984/command_validator/forbidden_reopening_validation.rb +29 -0
  7. data/lib/console1984/command_validator/parsed_command.rb +64 -0
  8. data/lib/console1984/command_validator/suspicious_terms_validation.rb +22 -0
  9. data/lib/console1984/command_validator.rb +71 -0
  10. data/lib/console1984/config.rb +9 -5
  11. data/lib/console1984/engine.rb +2 -0
  12. data/lib/console1984/errors.rb +10 -1
  13. data/lib/console1984/{protected_auditable_tables.rb → ext/active_record/protected_auditable_tables.rb} +2 -4
  14. data/lib/console1984/ext/core/object.rb +42 -0
  15. data/lib/console1984/ext/irb/commands.rb +16 -0
  16. data/lib/console1984/{protected_context.rb → ext/irb/context.rb} +5 -5
  17. data/lib/console1984/{protected_tcp_socket.rb → ext/socket/tcp_socket.rb} +3 -3
  18. data/lib/console1984/freezeable.rb +15 -5
  19. data/lib/console1984/{supervisor/input_output.rb → input_output.rb} +8 -2
  20. data/lib/console1984/messages.rb +0 -10
  21. data/lib/console1984/shield/modes/protected.rb +27 -0
  22. data/lib/console1984/shield/modes/unprotected.rb +8 -0
  23. data/lib/console1984/shield/modes.rb +60 -0
  24. data/lib/console1984/shield.rb +86 -0
  25. data/lib/console1984/supervisor.rb +24 -33
  26. data/lib/console1984/version.rb +1 -1
  27. data/lib/console1984.rb +36 -17
  28. metadata +61 -14
  29. data/config/routes.rb +0 -9
  30. data/lib/console1984/commands.rb +0 -16
  31. data/lib/console1984/protected_object.rb +0 -15
  32. data/lib/console1984/supervisor/accesses/protected.rb +0 -12
  33. data/lib/console1984/supervisor/accesses/unprotected.rb +0 -7
  34. data/lib/console1984/supervisor/accesses.rb +0 -41
  35. data/lib/console1984/supervisor/executor.rb +0 -65
  36. data/lib/console1984/supervisor/protector.rb +0 -55
@@ -1,3 +1,3 @@
1
1
  module Console1984
2
- VERSION = '0.1.6'
2
+ VERSION = '0.1.7'
3
3
  end
data/lib/console1984.rb CHANGED
@@ -4,38 +4,57 @@ require "zeitwerk"
4
4
  class_loader = Zeitwerk::Loader.for_gem
5
5
  class_loader.setup
6
6
 
7
+ # = Console 1984
8
+ #
9
+ # Console1984 is an IRB-based Rails console extension that does
10
+ # three things:
11
+ #
12
+ # * Record console sessions with their user, reason and commands.
13
+ # * Protect encrypted data by showing the ciphertexts when you visualize it.
14
+ # * Protect access to external systems that contain sensitive information (such as Redis
15
+ # or Elasticsearch).
16
+ #
17
+ # == Session logging
18
+ #
19
+ # The console will record the session, its user and the commands entered. The logic to
20
+ # persist sessions is handled by the configured session logger, which is
21
+ # Console1984::SessionsLogger::Database by default.
22
+ #
23
+ # == Execution of commands
24
+ #
25
+ # The console will work in two modes:
26
+ #
27
+ # * Protected: It won't show encrypted information (it will show the ciphertexts instead)
28
+ # and it won't allow connections to protected urls.
29
+ # * Unprotected: it allows access to encrypted information and protected urls. The commands
30
+ # executed in this mode as flagged as sensitive.
31
+ #
32
+ # Console1984::CommandExecutor handles the execution of commands applying the corresponding
33
+ # protection mechanisms.´
34
+ #
35
+ # == Internal tampering prevention
36
+ #
37
+ # Finally, console1984 includes protection mechanisms against internal tampering while using
38
+ # the console. For example, to prevent the user from deleting audit trails. See
39
+ # Console1984::Shield and Console1984::CommandValidator to learn more.
7
40
  module Console1984
8
41
  include Messages, Freezeable
9
42
 
10
43
  mattr_accessor :supervisor, default: Supervisor.new
44
+
11
45
  mattr_reader :config, default: Config.new
12
- mattr_accessor :class_loader
13
46
 
14
- thread_mattr_accessor :currently_protected_urls, default: []
47
+ mattr_accessor :class_loader
15
48
 
16
49
  class << self
17
50
  Config::PROPERTIES.each do |property|
18
51
  delegate property, to: :config
19
52
  end
20
53
 
54
+ # Returns whether the console is currently running in protected mode or not.
21
55
  def running_protected_environment?
22
56
  protected_environments.collect(&:to_sym).include?(Rails.env.to_sym)
23
57
  end
24
-
25
- def protecting(&block)
26
- protecting_connections do
27
- ActiveRecord::Encryption.protecting_encrypted_data(&block)
28
- end
29
- end
30
-
31
- private
32
- def protecting_connections
33
- old_currently_protected_urls = self.currently_protected_urls
34
- self.currently_protected_urls = protected_urls
35
- yield
36
- ensure
37
- self.currently_protected_urls = old_currently_protected_urls
38
- end
39
58
  end
40
59
  end
41
60
 
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.6
4
+ version: 0.1.7
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-08-29 00:00:00.000000000 Z
11
+ date: 2021-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: benchmark-ips
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +150,34 @@ dependencies:
136
150
  - - ">="
137
151
  - !ruby/object:Gem::Version
138
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: pg
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: mysql2
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
139
181
  description:
140
182
  email:
141
183
  - jorge@basecamp.com
@@ -153,27 +195,32 @@ files:
153
195
  - app/models/console1984/session.rb
154
196
  - app/models/console1984/session/incineratable.rb
155
197
  - app/models/console1984/user.rb
156
- - config/routes.rb
198
+ - config/command_protections.yml
157
199
  - db/migrate/20210517203931_create_console1984_tables.rb
158
200
  - lib/console1984.rb
159
- - lib/console1984/commands.rb
201
+ - lib/console1984/command_executor.rb
202
+ - lib/console1984/command_validator.rb
203
+ - lib/console1984/command_validator/forbidden_constant_reference_validation.rb
204
+ - lib/console1984/command_validator/forbidden_reopening_validation.rb
205
+ - lib/console1984/command_validator/parsed_command.rb
206
+ - lib/console1984/command_validator/suspicious_terms_validation.rb
160
207
  - lib/console1984/config.rb
161
208
  - lib/console1984/engine.rb
162
209
  - lib/console1984/errors.rb
210
+ - lib/console1984/ext/active_record/protected_auditable_tables.rb
211
+ - lib/console1984/ext/core/object.rb
212
+ - lib/console1984/ext/irb/commands.rb
213
+ - lib/console1984/ext/irb/context.rb
214
+ - lib/console1984/ext/socket/tcp_socket.rb
163
215
  - lib/console1984/freezeable.rb
216
+ - lib/console1984/input_output.rb
164
217
  - lib/console1984/messages.rb
165
- - lib/console1984/protected_auditable_tables.rb
166
- - lib/console1984/protected_context.rb
167
- - lib/console1984/protected_object.rb
168
- - lib/console1984/protected_tcp_socket.rb
169
218
  - lib/console1984/sessions_logger/database.rb
219
+ - lib/console1984/shield.rb
220
+ - lib/console1984/shield/modes.rb
221
+ - lib/console1984/shield/modes/protected.rb
222
+ - lib/console1984/shield/modes/unprotected.rb
170
223
  - lib/console1984/supervisor.rb
171
- - lib/console1984/supervisor/accesses.rb
172
- - lib/console1984/supervisor/accesses/protected.rb
173
- - lib/console1984/supervisor/accesses/unprotected.rb
174
- - lib/console1984/supervisor/executor.rb
175
- - lib/console1984/supervisor/input_output.rb
176
- - lib/console1984/supervisor/protector.rb
177
224
  - lib/console1984/username/env_resolver.rb
178
225
  - lib/console1984/version.rb
179
226
  - test/fixtures/console1984/commands.yml
data/config/routes.rb DELETED
@@ -1,9 +0,0 @@
1
- Console1984::Engine.routes.draw do
2
- resources :sessions, only: %i[ index show ] do
3
- resources :audits, only: %i[ create update ]
4
- end
5
-
6
- resource :filtered_sessions, only: %i[ update ]
7
-
8
- root to: "sessions#index"
9
- end
@@ -1,16 +0,0 @@
1
- module Console1984::Commands
2
- include Console1984::Freezeable
3
-
4
- def decrypt!
5
- supervisor.enable_access_to_encrypted_content
6
- end
7
-
8
- def encrypt!
9
- supervisor.disable_access_to_encrypted_content
10
- end
11
-
12
- private
13
- def supervisor
14
- Console1984.supervisor
15
- end
16
- end
@@ -1,15 +0,0 @@
1
- module Console1984::ProtectedObject
2
- extend ActiveSupport::Concern
3
-
4
- include Console1984::Freezeable
5
-
6
- class_methods do
7
- def const_get(*arguments)
8
- if Console1984.supervisor.executing_user_command? && arguments.first.to_s =~ /Console1984|ActiveRecord/
9
- raise Console1984::Errors::ForbiddenCommand
10
- else
11
- super
12
- end
13
- end
14
- end
15
- end
@@ -1,12 +0,0 @@
1
- class Console1984::Supervisor::Accesses::Protected
2
- include Console1984::Freezeable
3
-
4
- def execute(&block)
5
- Console1984.protecting(&block)
6
- end
7
-
8
- private
9
- def null_encryptor
10
- @null_encryptor ||= ActiveRecord::Encryption::NullEncryptor.new
11
- end
12
- end
@@ -1,7 +0,0 @@
1
- class Console1984::Supervisor::Accesses::Unprotected
2
- include Console1984::Freezeable
3
-
4
- def execute(&block)
5
- block.call
6
- end
7
- end
@@ -1,41 +0,0 @@
1
- module Console1984::Supervisor::Accesses
2
- include Console1984::Messages, Console1984::Freezeable
3
-
4
- PROTECTED_ACCESS = Protected.new
5
- UNPROTECTED_ACCESS = Unprotected.new
6
-
7
- def enable_access_to_encrypted_content(silent: false)
8
- run_system_command do
9
- show_warning Console1984.enter_unprotected_encryption_mode_warning if !silent && protected_mode?
10
- justification = ask_for_value "\nBefore you can access personal information, you need to ask for and get explicit consent from the user(s). #{current_username}, where can we find this consent (a URL would be great)?"
11
- session_logger.start_sensitive_access justification
12
- nil
13
- end
14
- ensure
15
- @access = UNPROTECTED_ACCESS
16
- nil
17
- end
18
-
19
- def disable_access_to_encrypted_content(silent: false)
20
- run_system_command do
21
- show_warning Console1984.enter_protected_mode_warning if !silent && unprotected_mode?
22
- session_logger.end_sensitive_access
23
- nil
24
- end
25
- ensure
26
- @access = PROTECTED_ACCESS
27
- nil
28
- end
29
-
30
- def with_encryption_mode(&block)
31
- @access.execute(&block)
32
- end
33
-
34
- def unprotected_mode?
35
- @access.is_a?(Unprotected)
36
- end
37
-
38
- def protected_mode?
39
- !unprotected_mode?
40
- end
41
- end
@@ -1,65 +0,0 @@
1
- module Console1984::Supervisor::Executor
2
- extend ActiveSupport::Concern
3
-
4
- include Console1984::Freezeable
5
-
6
- def execute_supervised(commands, &block)
7
- run_system_command { session_logger.before_executing commands }
8
- validate_commands(commands)
9
- execute(&block)
10
- rescue Console1984::Errors::ForbiddenCommand, Console1984::Errors::ForbiddenCodeManipulation, FrozenError
11
- flag_forbidden(commands)
12
- rescue FrozenError
13
- flag_forbidden(commands)
14
- ensure
15
- run_system_command { session_logger.after_executing commands }
16
- end
17
-
18
- def execute(&block)
19
- run_user_command do
20
- with_encryption_mode(&block)
21
- end
22
- end
23
-
24
- def executing_user_command?
25
- @executing_user_command
26
- end
27
-
28
- private
29
- def flag_forbidden(commands)
30
- puts "Forbidden command attempted: #{commands.join("\n")}"
31
- run_system_command { session_logger.suspicious_commands_attempted commands }
32
- nil
33
- end
34
-
35
- def run_user_command(&block)
36
- run_command true, &block
37
- end
38
-
39
- def run_system_command(&block)
40
- run_command false, &block
41
- end
42
-
43
- def validate_commands(commands)
44
- if Array(commands).find { |command| forbidden_command?(command) }
45
- raise Console1984::Errors::ForbiddenCommand
46
- end
47
- end
48
-
49
- def forbidden_command?(command)
50
- # This is a first protection layer. Very simple for now. We'll likely make this
51
- # more sophisticated and configurable in future versions.
52
- #
53
- # We can't use our +Freezable+ concern in ActiveRecord since it relies on code
54
- # generation on the fly.
55
- command =~ /Console1984|console_1984|(class|module)\s+ActiveRecord::/
56
- end
57
-
58
- def run_command(run_by_user, &block)
59
- original_value = @executing_user_command
60
- @executing_user_command = run_by_user
61
- block.call
62
- ensure
63
- @executing_user_command = original_value
64
- end
65
- end
@@ -1,55 +0,0 @@
1
- module Console1984::Supervisor::Protector
2
- extend ActiveSupport::Concern
3
-
4
- include Console1984::Freezeable
5
-
6
- private
7
- def extend_protected_systems
8
- extend_object
9
- extend_irb
10
- extend_active_record
11
- extend_socket_classes
12
- end
13
-
14
- def extend_object
15
- Object.prepend Console1984::ProtectedObject
16
- end
17
-
18
- def extend_irb
19
- IRB::Context.prepend(Console1984::ProtectedContext)
20
- Rails::ConsoleMethods.include(Console1984::Commands)
21
- end
22
-
23
- ACTIVE_RECORD_CONNECTION_ADAPTERS = %w[ActiveRecord::ConnectionAdapters::Mysql2Adapter ActiveRecord::ConnectionAdapters::PostgreSQLAdapter ActiveRecord::ConnectionAdapters::SQLite3Adapter]
24
-
25
- def extend_active_record
26
- ACTIVE_RECORD_CONNECTION_ADAPTERS.each do |class_string|
27
- if Object.const_defined?(class_string)
28
- klass = class_string.constantize
29
- klass.prepend(Console1984::ProtectedAuditableTables)
30
- klass.include(Console1984::Freezeable)
31
- end
32
- end
33
- end
34
-
35
- def extend_socket_classes
36
- socket_classes = [TCPSocket, OpenSSL::SSL::SSLSocket]
37
- OpenSSL::SSL::SSLSocket.include(SSLSocketRemoteAddress)
38
-
39
- if defined?(Redis::Connection)
40
- socket_classes.push(*[Redis::Connection::TCPSocket, Redis::Connection::SSLSocket])
41
- end
42
-
43
- socket_classes.compact.each do |socket_klass|
44
- socket_klass.prepend Console1984::ProtectedTcpSocket
45
- socket_klass.freeze
46
- end
47
- end
48
-
49
- module SSLSocketRemoteAddress
50
- # Make it serve remote address as TCPSocket so that our extension works for it
51
- def remote_address
52
- Addrinfo.getaddrinfo(hostname, 443).first
53
- end
54
- end
55
- end