tcell_agent 2.0.0 → 2.5.0

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +2 -2
  3. data/bin/tcell_agent +41 -150
  4. data/lib/tcell_agent/agent.rb +87 -52
  5. data/lib/tcell_agent/config_initializer.rb +63 -0
  6. data/lib/tcell_agent/configuration.rb +72 -267
  7. data/lib/tcell_agent/hooks/login_fraud.rb +1 -1
  8. data/lib/tcell_agent/instrument_servers.rb +14 -18
  9. data/lib/tcell_agent/instrumentation/cmdi.rb +47 -15
  10. data/lib/tcell_agent/instrumentation/lfi.rb +72 -15
  11. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_2/file.rb +21 -0
  12. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_2/io.rb +75 -0
  13. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_2/kernel.rb +80 -0
  14. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_3/file.rb +21 -0
  15. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_3/io.rb +75 -0
  16. data/lib/tcell_agent/instrumentation/monkey_patches/ruby_3/kernel.rb +80 -0
  17. data/lib/tcell_agent/instrumentation.rb +14 -6
  18. data/lib/tcell_agent/logger.rb +3 -4
  19. data/lib/tcell_agent/policies/command_injection_policy.rb +1 -1
  20. data/lib/tcell_agent/policies/dataloss_policy.rb +15 -8
  21. data/lib/tcell_agent/policies/headers_policy.rb +2 -2
  22. data/lib/tcell_agent/policies/patches_policy.rb +8 -4
  23. data/lib/tcell_agent/policies/policies_manager.rb +1 -0
  24. data/lib/tcell_agent/policies/policy_polling.rb +4 -3
  25. data/lib/tcell_agent/rails/auth/authlogic.rb +49 -44
  26. data/lib/tcell_agent/rails/auth/authlogic_helper.rb +20 -0
  27. data/lib/tcell_agent/rails/auth/devise.rb +103 -102
  28. data/lib/tcell_agent/rails/auth/devise_helper.rb +29 -0
  29. data/lib/tcell_agent/rails/auth/doorkeeper.rb +54 -57
  30. data/lib/tcell_agent/{userinfo.rb → rails/auth/userinfo.rb} +0 -0
  31. data/lib/tcell_agent/rails/better_ip.rb +7 -19
  32. data/lib/tcell_agent/rails/csrf_exception.rb +0 -8
  33. data/lib/tcell_agent/rails/dlp/process_request.rb +5 -0
  34. data/lib/tcell_agent/rails/dlp.rb +58 -56
  35. data/lib/tcell_agent/rails/dlp_handler.rb +9 -10
  36. data/lib/tcell_agent/rails/js_agent_insert.rb +2 -3
  37. data/lib/tcell_agent/rails/middleware/context_middleware.rb +2 -1
  38. data/lib/tcell_agent/rails/middleware/global_middleware.rb +3 -4
  39. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +1 -0
  40. data/lib/tcell_agent/rails/{on_start.rb → railties/tcell_agent_railties.rb} +9 -16
  41. data/lib/tcell_agent/rails/railties/tcell_agent_unicorn_railties.rb +8 -0
  42. data/lib/tcell_agent/rails/routes/grape.rb +5 -12
  43. data/lib/tcell_agent/rails/routes.rb +6 -9
  44. data/lib/tcell_agent/rails/settings_reporter.rb +3 -6
  45. data/lib/tcell_agent/rails/tcell_body_proxy.rb +4 -7
  46. data/lib/tcell_agent/routes/table.rb +3 -0
  47. data/lib/tcell_agent/rust/agent_config.rb +62 -33
  48. data/lib/tcell_agent/rust/{libtcellagent-4.14.0.so → libtcellagent-alpine.so} +0 -0
  49. data/lib/tcell_agent/rust/{libtcellagent-4.14.0.dylib → libtcellagent-x64.dll} +0 -0
  50. data/lib/tcell_agent/rust/{libtcellagent-alpine-4.14.0.so → libtcellagent.dylib} +0 -0
  51. data/lib/tcell_agent/rust/libtcellagent.so +0 -0
  52. data/lib/tcell_agent/rust/models.rb +9 -0
  53. data/lib/tcell_agent/rust/native_agent.rb +61 -51
  54. data/lib/tcell_agent/rust/native_library.rb +8 -10
  55. data/lib/tcell_agent/sensor_events/server_agent.rb +3 -100
  56. data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +1 -0
  57. data/lib/tcell_agent/servers/puma.rb +30 -13
  58. data/lib/tcell_agent/servers/rack_puma_handler.rb +33 -0
  59. data/lib/tcell_agent/servers/rails_server.rb +4 -4
  60. data/lib/tcell_agent/servers/unicorn.rb +1 -1
  61. data/lib/tcell_agent/servers/webrick.rb +12 -3
  62. data/lib/tcell_agent/settings_reporter.rb +0 -93
  63. data/lib/tcell_agent/sinatra.rb +1 -0
  64. data/lib/tcell_agent/tcell_context.rb +16 -7
  65. data/lib/tcell_agent/utils/headers.rb +0 -1
  66. data/lib/tcell_agent/utils/strings.rb +2 -2
  67. data/lib/tcell_agent/version.rb +1 -1
  68. data/lib/tcell_agent.rb +8 -16
  69. data/spec/cruby_spec_helper.rb +26 -0
  70. data/spec/lib/tcell_agent/configuration_spec.rb +62 -212
  71. data/spec/lib/tcell_agent/instrument_servers_spec.rb +95 -0
  72. data/spec/lib/tcell_agent/instrumentation/cmdi/io_cmdi_spec.rb +2 -2
  73. data/spec/lib/tcell_agent/{cmdi_spec.rb → instrumentation/cmdi_spec.rb} +50 -0
  74. data/spec/lib/tcell_agent/instrumentation/lfi/file_lfi_spec.rb +211 -272
  75. data/spec/lib/tcell_agent/instrumentation/lfi/io_lfi_spec.rb +213 -223
  76. data/spec/lib/tcell_agent/instrumentation/lfi/kernel_lfi_spec.rb +95 -61
  77. data/spec/lib/tcell_agent/instrumentation/lfi_spec.rb +120 -2
  78. data/spec/lib/tcell_agent/patches_spec.rb +2 -1
  79. data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +1 -2
  80. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +5 -6
  81. data/spec/lib/tcell_agent/policies/patches_policy_spec.rb +21 -2
  82. data/spec/lib/tcell_agent/policies/policies_manager_spec.rb +1 -1
  83. data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +13 -8
  84. data/spec/lib/tcell_agent/rails/better_ip_spec.rb +9 -11
  85. data/spec/lib/tcell_agent/rails/csrf_exception_spec.rb +6 -6
  86. data/spec/lib/tcell_agent/rails/dlp_spec.rb +1 -0
  87. data/spec/lib/tcell_agent/rails/js_agent_insert_spec.rb +10 -2
  88. data/spec/lib/tcell_agent/rails/middleware/tcell_body_proxy_spec.rb +2 -1
  89. data/spec/lib/tcell_agent/rails/routes/route_id_spec.rb +4 -4
  90. data/spec/lib/tcell_agent/rust/agent_config_spec.rb +27 -0
  91. data/spec/lib/tcell_agent/settings_reporter_spec.rb +2 -89
  92. data/spec/lib/tcell_agent/tcell_context_spec.rb +6 -5
  93. data/spec/spec_helper.rb +9 -1
  94. data/spec/support/builders.rb +8 -7
  95. data/spec/support/server_mocks/passenger_mock.rb +7 -0
  96. data/spec/support/server_mocks/puma_mock.rb +21 -0
  97. data/spec/support/server_mocks/rails_mock.rb +7 -0
  98. data/spec/support/server_mocks/thin_mock.rb +7 -0
  99. data/spec/support/server_mocks/unicorn_mock.rb +11 -0
  100. data/spec/support/shared_spec.rb +29 -0
  101. data/tcell_agent.gemspec +14 -14
  102. metadata +46 -29
  103. data/Rakefile +0 -18
  104. data/lib/tcell_agent/authlogic.rb +0 -23
  105. data/lib/tcell_agent/config/unknown_options.rb +0 -119
  106. data/lib/tcell_agent/devise.rb +0 -33
  107. data/lib/tcell_agent/instrumentation/monkey_patches/file.rb +0 -25
  108. data/lib/tcell_agent/instrumentation/monkey_patches/io.rb +0 -123
  109. data/lib/tcell_agent/instrumentation/monkey_patches/kernel.rb +0 -159
  110. data/lib/tcell_agent/rails/start_agent_after_initializers.rb +0 -12
  111. data/lib/tcell_agent/rust/tcellagent-4.14.0.dll +0 -0
  112. data/spec/lib/tcell_agent/config/unknown_options_spec.rb +0 -195
@@ -1,159 +0,0 @@
1
- module Kernel
2
- class << self
3
- alias_method :tcell_original_1_open, :open
4
- def open(*args, &block)
5
- path, mode = TCellAgent::Instrumentation::Lfi.extract_path_mode(*args)
6
-
7
- if path && TCellAgent::Instrumentation::Lfi.block_file_access?(path, mode)
8
- raise IOError, "tCell.io Agent: Attempted access to file #{path} with mode #{mode} denied"
9
- end
10
-
11
- cmd = TCellAgent::Cmdi.parse_command_from_open(*args)
12
- if cmd && TCellAgent::Cmdi.block_command?(cmd)
13
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
14
- end
15
-
16
- tcell_original_1_open(*args, &block)
17
- end
18
-
19
- alias_method :tcell_original_1_gets, :gets
20
- def gets(*args, &block)
21
- path, mode = TCellAgent::Instrumentation::Lfi.extract_path_mode_argf
22
-
23
- if TCellAgent::Instrumentation::Lfi.block_file_access?(path, mode)
24
- raise IOError, "tCell.io Agent: Attempted access to file #{path} with mode #{mode} denied"
25
- end
26
-
27
- tcell_original_1_gets(*args, &block)
28
- end
29
-
30
- alias_method :tcell_original_readline, :readline
31
- def readline(*args, &block)
32
- path, mode = TCellAgent::Instrumentation::Lfi.extract_path_mode_argf
33
-
34
- if TCellAgent::Instrumentation::Lfi.block_file_access?(path, mode)
35
- raise IOError, "tCell.io Agent: Attempted access to file #{path} with mode #{mode} denied"
36
- end
37
-
38
- tcell_original_readline(*args, &block)
39
- end
40
-
41
- alias_method :tcell_original_1_spawn, :spawn
42
- def spawn(*args)
43
- cmd = TCellAgent::Cmdi.parse_command(*args)
44
- if TCellAgent::Cmdi.block_command?(cmd)
45
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
46
- end
47
-
48
- tcell_original_1_spawn(*args)
49
- end
50
-
51
- alias_method :tcell_original_1_system, :system
52
- def system(*args)
53
- cmd = TCellAgent::Cmdi.parse_command(*args)
54
- if TCellAgent::Cmdi.block_command?(cmd)
55
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
56
- end
57
-
58
- tcell_original_1_system(*args)
59
- end
60
- end
61
-
62
- alias_method :tcell_original_backtick, :`
63
- def `(cmd)
64
- if TCellAgent::Cmdi.block_command?(cmd)
65
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
66
- end
67
-
68
- tcell_original_backtick(cmd)
69
- end
70
-
71
- alias_method :tcell_original_2_open, :open
72
- def open(*args, &block)
73
- path, mode = TCellAgent::Instrumentation::Lfi.extract_path_mode(*args)
74
-
75
- if path && TCellAgent::Instrumentation::Lfi.block_file_access?(path, mode)
76
- raise IOError, "tCell.io Agent: Attempted access to file #{path} with mode #{mode} denied"
77
- end
78
-
79
- cmd = TCellAgent::Cmdi.parse_command_from_open(*args)
80
- if cmd && TCellAgent::Cmdi.block_command?(cmd)
81
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
82
- end
83
-
84
- tcell_original_2_open(*args, &block)
85
- end
86
-
87
- alias_method :tcell_original_2_gets, :gets
88
- def gets(*args, &block)
89
- path, mode = TCellAgent::Instrumentation::Lfi.extract_path_mode_argf
90
-
91
- if TCellAgent::Instrumentation::Lfi.block_file_access?(path, mode)
92
- raise IOError, "tCell.io Agent: Attempted access to file #{path} with mode #{mode} denied"
93
- end
94
-
95
- tcell_original_2_gets(*args, &block)
96
- end
97
-
98
- alias_method :tcell_original_readline, :readline
99
- def readline(*args, &block)
100
- path, mode = TCellAgent::Instrumentation::Lfi.extract_path_mode_argf
101
-
102
- if TCellAgent::Instrumentation::Lfi.block_file_access?(path, mode)
103
- raise IOError, "tCell.io Agent: Attempted access to file #{path} with mode #{mode} denied"
104
- end
105
-
106
- tcell_original_readline(*args, &block)
107
- end
108
-
109
- alias_method :tcell_original_2_spawn, :spawn
110
- def spawn(*args)
111
- cmd = TCellAgent::Cmdi.parse_command(*args)
112
- if TCellAgent::Cmdi.block_command?(cmd)
113
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
114
- end
115
-
116
- tcell_original_2_spawn(*args)
117
- end
118
-
119
- alias_method :tcell_original_2_system, :system
120
- def system(*args)
121
- cmd = TCellAgent::Cmdi.parse_command(*args)
122
- if TCellAgent::Cmdi.block_command?(cmd)
123
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
124
- end
125
-
126
- tcell_original_2_system(*args)
127
- end
128
- end
129
-
130
- if TCellAgent.configuration.should_instrument_cmdi_exec?
131
- module Kernel
132
- class << self
133
- alias_method :tcell_original_exec, :exec
134
- def exec(*args)
135
- cmd = TCellAgent::Cmdi.parse_command(*args)
136
- if TCellAgent::Cmdi.block_command?(cmd)
137
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
138
- end
139
-
140
- tcell_original_exec(*args)
141
- end
142
- end
143
-
144
- alias_method :tcell_original_exec, :exec
145
-
146
- private
147
-
148
- def exec(*args)
149
- cmd = TCellAgent::Cmdi.parse_command(*args)
150
- if TCellAgent::Cmdi.block_command?(cmd)
151
- raise "tCell.io Agent: Command not allowed by policy: #{cmd}"
152
- end
153
-
154
- tcell_original_exec(*args)
155
- end
156
- end
157
- else
158
- TCellAgent.logger.debug('Disabling cmdi Kernel::exec instrumentation', 'TCellAgent::Cmdi')
159
- end
@@ -1,12 +0,0 @@
1
- module TCellAgent
2
- class TCellAgentStartupRailtie < Rails::Railtie
3
- # TCellAgent config can be specified thru Rails initializer's
4
- # (https://guides.rubyonrails.org/v2.3/configuring.html#using-initializers)
5
- # so those need to run first before the agent is started
6
- initializer :start_tcell_agent,
7
- :after => :load_config_initializers,
8
- :before => :tcell_instrument_auth_frameworks do |_app|
9
- TCellAgent.thread_agent.start('Unicorn')
10
- end
11
- end
12
- end
@@ -1,195 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module TCellAgent
4
- module Config
5
- describe Validate do
6
- describe '.get_unknown_options' do
7
- context 'with an unknown tcell environment variable set' do
8
- it 'should return a message about the unknown variable' do
9
- orig_allow_ap = ENV.fetch('TCELL_AGENT_ALLOW_PAYLOADS', nil)
10
- orig_demomode = ENV.fetch('TCELL_DEMOMODE', nil)
11
- orig_agent_home = ENV.fetch('TCELL_AGENT_HOME', nil)
12
- orig_agent_log_dir = ENV.fetch('TCELL_AGENT_LOG_DIR', nil)
13
- orig_agent_config = ENV.fetch('TCELL_AGENT_CONFIG', nil)
14
- orig_agent_app_id = ENV.fetch('TCELL_AGENT_APP_ID', nil)
15
- orig_agent_api_key = ENV.fetch('TCELL_AGENT_API_KEY', nil)
16
- orig_agent_host_identifier = ENV.fetch('TCELL_AGENT_HOST_IDENTIFIER', nil)
17
- orig_input_url = ENV.fetch('TCELL_INPUT_URL', nil)
18
- orig_hmac_key = ENV.fetch('TCELL_HMAC_KEY', nil)
19
- orig_api_url = ENV.fetch('TCELL_API_URL', nil)
20
- orig_password_hmac_key = ENV.fetch('TCELL_PASSWORD_HMAC_KEY', nil)
21
-
22
- ENV['TCELL_HACK'] = 'hack the system'
23
- ENV['TCELL_AGENT_ALLOW_PAYLOADS'] = 'valid'
24
- ENV['TCELL_DEMOMODE'] = 'valid'
25
- ENV['TCELL_AGENT_HOME'] = 'valid'
26
- ENV['TCELL_AGENT_LOG_DIR'] = 'valid'
27
- ENV['TCELL_AGENT_CONFIG'] = 'valid'
28
- ENV['TCELL_AGENT_APP_ID'] = 'valid'
29
- ENV['TCELL_AGENT_API_KEY'] = 'valid'
30
- ENV['TCELL_AGENT_HOST_IDENTIFIER'] = 'valid'
31
- ENV['TCELL_INPUT_URL'] = 'valid'
32
- ENV['TCELL_HMAC_KEY'] = 'valid'
33
- ENV['TCELL_API_URL'] = 'valid'
34
- ENV['TCELL_PASSWORD_HMAC_KEY'] = 'valid'
35
-
36
- messages = Validate.get_unknown_options(nil)
37
-
38
- ENV.delete 'TCELL_HACK'
39
-
40
- if orig_allow_ap
41
- ENV['TCELL_AGENT_ALLOW_PAYLOADS'] = orig_allow_ap
42
- else
43
- ENV.delete 'TCELL_AGENT_ALLOW_PAYLOADS'
44
- end
45
- if orig_demomode
46
- ENV['TCELL_DEMOMODE'] = orig_demomode
47
- else
48
- ENV.delete 'TCELL_DEMOMODE'
49
- end
50
- if orig_agent_home
51
- ENV['TCELL_AGENT_HOME'] = orig_agent_home
52
- else
53
- ENV.delete 'TCELL_AGENT_HOME'
54
- end
55
- if orig_agent_log_dir
56
- ENV['TCELL_AGENT_LOG_DIR'] = orig_agent_log_dir
57
- else
58
- ENV.delete 'TCELL_AGENT_LOG_DIR'
59
- end
60
- if orig_agent_config
61
- ENV['TCELL_AGENT_CONFIG'] = orig_agent_config
62
- else
63
- ENV.delete 'TCELL_AGENT_CONFIG'
64
- end
65
- if orig_agent_app_id
66
- ENV['TCELL_AGENT_APP_ID'] = orig_agent_app_id
67
- else
68
- ENV.delete 'TCELL_AGENT_APP_ID'
69
- end
70
- if orig_agent_api_key
71
- ENV['TCELL_AGENT_API_KEY'] = orig_agent_api_key
72
- else
73
- ENV.delete 'TCELL_AGENT_API_KEY'
74
- end
75
- if orig_agent_host_identifier
76
- ENV['TCELL_AGENT_HOST_IDENTIFIER'] = orig_agent_host_identifier
77
- else
78
- ENV.delete 'TCELL_AGENT_HOST_IDENTIFIER'
79
- end
80
- if orig_input_url
81
- ENV['TCELL_INPUT_URL'] = orig_input_url
82
- else
83
- ENV.delete 'TCELL_INPUT_URL'
84
- end
85
- if orig_hmac_key
86
- ENV['TCELL_HMAC_KEY'] = orig_hmac_key
87
- else
88
- ENV.delete 'TCELL_HMAC_KEY'
89
- end
90
- if orig_password_hmac_key
91
- ENV['TCELL_PASSWORD_HMAC_KEY'] = orig_password_hmac_key
92
- else
93
- ENV.delete 'TCELL_PASSWORD_HMAC_KEY'
94
- end
95
- if orig_api_url
96
- ENV['TCELL_API_URL'] = orig_api_url
97
- else
98
- ENV.delete 'TCELL_API_URL'
99
- end
100
-
101
- expect(messages.sort).to eq(
102
- [
103
- 'Unrecognized environment parameter (TCELL_*) found: TCELL_HACK'
104
- ]
105
- )
106
- end
107
- end
108
-
109
- context 'with a config json with all options including some extra ones' do
110
- it 'should report the extra options in messages' do
111
- config_json = {
112
- 'first_level' => 'boo',
113
- 'version' => 1,
114
- 'applications' => [
115
- {
116
- 'second_level' => 'boo',
117
- 'name' => 'name',
118
- 'app_id' => 'app id',
119
- 'api_key' => 'api key',
120
- 'fetch_policies_from_tcell' => true,
121
- 'preload_policy_filename' => 'preload policy filename',
122
- 'log_dir' => 'custom log dir',
123
- 'logging_options' => {
124
- 'logging_level' => 'boo',
125
- 'enabled' => true,
126
- 'level' => 'DEBUG',
127
- 'filename' => 'filename'
128
- },
129
- 'tcell_api_url' => 'tcell api url',
130
- 'tcell_input_url' => 'tcell input url',
131
- 'host_identifier' => 'host identifier',
132
- 'hipaaSafeMode' => 'hipaa safe mode',
133
- 'hmac_key' => 'hmac key',
134
- 'password_hmac_key' => 'password_hmac_key',
135
- 'js_agent_api_base_url' => 'js agent api base url',
136
- 'js_agent_url' => 'js agent url',
137
- 'max_csp_header_bytes' => 512,
138
- 'event_batch_size_limit' => 50,
139
- 'allow_payloads' => true,
140
- 'data_exposure' => {
141
- 'data_ex_level' => 'boo',
142
- 'max_data_ex_db_records_per_request' => 10_000
143
- },
144
- 'reverse_proxy' => true,
145
- 'reverse_proxy_ip_address_header' => 'reverse proxy ip address header',
146
- 'demomode' => true,
147
- # Ruby only
148
- 'disable_all' => false,
149
- 'enabled' => true,
150
- 'enable_event_manager' => true,
151
- 'enable_policy_polling' => true,
152
- 'enable_instrumentation' => true,
153
- 'enable_intercept_requests' => true,
154
- 'instrument_for_events' => true,
155
- 'enabled_instrumentations' => {
156
- 'enabled_instrumentations_level' => 'blah',
157
- 'doorkeeper' => true,
158
- 'devise' => true,
159
- 'authlogic' => true
160
- }
161
- }
162
- ]
163
- }
164
-
165
- messages = Validate.get_unknown_options(config_json)
166
-
167
- expect(messages.sort).to eq(
168
- [
169
- 'Unrecognized config setting key: data_ex_level',
170
- 'Unrecognized config setting key: enabled_instrumentations_level',
171
- 'Unrecognized config setting key: first_level',
172
- 'Unrecognized config setting key: logging_level',
173
- 'Unrecognized config setting key: second_level'
174
- ]
175
- )
176
- end
177
- end
178
-
179
- context 'with a config json that has more than one application' do
180
- it 'should report the misconfiguration' do
181
- config_json = { 'version' => 1, 'applications' => [{}, {}] }
182
-
183
- messages = Validate.get_unknown_options(config_json)
184
-
185
- expect(messages.sort).to eq(
186
- [
187
- 'Multiple applications detected in config file'
188
- ]
189
- )
190
- end
191
- end
192
- end
193
- end
194
- end
195
- end