console1984 0.1.6 → 0.1.7

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