tcell_agent 2.0.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/bin/tcell_agent +42 -146
  3. data/lib/tcell_agent.rb +8 -16
  4. data/lib/tcell_agent/agent.rb +76 -46
  5. data/lib/tcell_agent/config_initializer.rb +66 -0
  6. data/lib/tcell_agent/configuration.rb +72 -267
  7. data/lib/tcell_agent/instrument_servers.rb +14 -18
  8. data/lib/tcell_agent/instrumentation/cmdi.rb +15 -15
  9. data/lib/tcell_agent/instrumentation/lfi.rb +21 -10
  10. data/lib/tcell_agent/instrumentation/monkey_patches/io.rb +20 -12
  11. data/lib/tcell_agent/instrumentation/monkey_patches/kernel.rb +45 -102
  12. data/lib/tcell_agent/logger.rb +1 -2
  13. data/lib/tcell_agent/policies/command_injection_policy.rb +1 -1
  14. data/lib/tcell_agent/rails/auth/authlogic.rb +49 -44
  15. data/lib/tcell_agent/rails/auth/authlogic_helper.rb +20 -0
  16. data/lib/tcell_agent/rails/auth/devise.rb +103 -102
  17. data/lib/tcell_agent/rails/auth/devise_helper.rb +29 -0
  18. data/lib/tcell_agent/rails/auth/doorkeeper.rb +54 -58
  19. data/lib/tcell_agent/{userinfo.rb → rails/auth/userinfo.rb} +0 -0
  20. data/lib/tcell_agent/rails/csrf_exception.rb +0 -8
  21. data/lib/tcell_agent/rails/dlp.rb +10 -8
  22. data/lib/tcell_agent/rails/middleware/global_middleware.rb +4 -1
  23. data/lib/tcell_agent/rails/{on_start.rb → railties/tcell_agent_railties.rb} +9 -16
  24. data/lib/tcell_agent/rails/railties/tcell_agent_unicorn_railties.rb +8 -0
  25. data/lib/tcell_agent/rails/routes.rb +6 -9
  26. data/lib/tcell_agent/rails/routes/grape.rb +4 -12
  27. data/lib/tcell_agent/rails/tcell_body_proxy.rb +0 -1
  28. data/lib/tcell_agent/rust/agent_config.rb +43 -32
  29. data/lib/tcell_agent/rust/{libtcellagent-4.14.0.dylib → libtcellagent-5.0.2.dylib} +0 -0
  30. data/lib/tcell_agent/rust/{libtcellagent-4.14.0.so → libtcellagent-5.0.2.so} +0 -0
  31. data/lib/tcell_agent/rust/{libtcellagent-alpine-4.14.0.so → libtcellagent-alpine-5.0.2.so} +0 -0
  32. data/lib/tcell_agent/rust/models.rb +9 -0
  33. data/lib/tcell_agent/rust/native_agent.rb +18 -0
  34. data/lib/tcell_agent/rust/native_library.rb +2 -1
  35. data/lib/tcell_agent/rust/{tcellagent-4.14.0.dll → tcellagent-5.0.2.dll} +0 -0
  36. data/lib/tcell_agent/servers/puma.rb +7 -7
  37. data/lib/tcell_agent/servers/rack_puma_handler.rb +23 -0
  38. data/lib/tcell_agent/servers/rails_server.rb +4 -4
  39. data/lib/tcell_agent/servers/unicorn.rb +1 -1
  40. data/lib/tcell_agent/servers/webrick.rb +0 -1
  41. data/lib/tcell_agent/settings_reporter.rb +0 -79
  42. data/lib/tcell_agent/tcell_context.rb +1 -1
  43. data/lib/tcell_agent/version.rb +1 -1
  44. data/spec/lib/tcell_agent/configuration_spec.rb +62 -212
  45. data/spec/lib/tcell_agent/instrument_servers_spec.rb +95 -0
  46. data/spec/lib/tcell_agent/{cmdi_spec.rb → instrumentation/cmdi_spec.rb} +50 -0
  47. data/spec/lib/tcell_agent/instrumentation/lfi/io_lfi_spec.rb +6 -0
  48. data/spec/lib/tcell_agent/instrumentation/lfi/kernel_lfi_spec.rb +19 -4
  49. data/spec/lib/tcell_agent/instrumentation/lfi_spec.rb +47 -2
  50. data/spec/lib/tcell_agent/rust/agent_config_spec.rb +27 -0
  51. data/spec/lib/tcell_agent/settings_reporter_spec.rb +0 -73
  52. data/spec/spec_helper.rb +6 -0
  53. data/spec/support/builders.rb +6 -6
  54. data/spec/support/server_mocks/passenger_mock.rb +7 -0
  55. data/spec/support/server_mocks/puma_mock.rb +17 -0
  56. data/spec/support/server_mocks/rails_mock.rb +7 -0
  57. data/spec/support/server_mocks/thin_mock.rb +7 -0
  58. data/spec/support/server_mocks/unicorn_mock.rb +11 -0
  59. metadata +29 -16
  60. data/lib/tcell_agent/authlogic.rb +0 -23
  61. data/lib/tcell_agent/config/unknown_options.rb +0 -119
  62. data/lib/tcell_agent/devise.rb +0 -33
  63. data/lib/tcell_agent/rails/start_agent_after_initializers.rb +0 -12
  64. data/spec/lib/tcell_agent/config/unknown_options_spec.rb +0 -195
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TCellAgent
4
+ class ConfigInitializer < Configuration
5
+ # Common config shared across agents
6
+ attr_accessor :app_id, :api_key, :tcell_api_url,
7
+ :tcell_input_url, :log_dir, :fetch_policies_from_tcell,
8
+ :preload_policy_filename, :reverse_proxy,
9
+ :reverse_proxy_ip_address_header, :host_identifier,
10
+ :hmac_key, :password_hmac_key,
11
+ :js_agent_url, :js_agent_api_base_url,
12
+ :max_csp_header_bytes, :allow_payloads
13
+
14
+ attr_reader :logging_options
15
+
16
+ # Ruby only config
17
+ attr_accessor :disable_all, :enabled, :enable_event_manager,
18
+ :enable_policy_polling,
19
+ :data_exposure
20
+
21
+ attr_reader :instrument_for_events,
22
+ :enable_instrumentation
23
+
24
+ # New config
25
+ attr_accessor :disabled_instrumentation, :instrument
26
+
27
+ def initialize
28
+ # ruby agent defaults
29
+ @logging_options = {}
30
+ end
31
+
32
+ def logging_options=(hash)
33
+ @logging_options = hash.each_with_object({}) do |(key, val), memo|
34
+ memo[key.to_sym] = val
35
+ end
36
+ end
37
+
38
+ # legacy config mapping
39
+ def enabled_instrumentations=(hash)
40
+ @disabled_instrumentation ||= []
41
+ hash.each do |key, val|
42
+ @disabled_instrumentation << key.to_s.strip.downcase unless TCellAgent.configuration.to_bool(val)
43
+ end
44
+ end
45
+
46
+ def agent_log_dir=(path)
47
+ @log_dir = path
48
+ end
49
+
50
+ def instrument_for_events=(bool)
51
+ @instrument = bool
52
+ end
53
+
54
+ def enable_instrumentation=(bool)
55
+ @instrument = bool
56
+ end
57
+
58
+ def enable_intercept_requests=(bool)
59
+ @disabled_instrumentation << 'intercept_requests' unless TCellAgent.configuration.to_bool(bool)
60
+ end
61
+
62
+ def get_config_file_dir
63
+ super
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # See the file "LICENSE" for the full license governing this code.
2
4
  require 'fileutils'
3
5
  require 'json'
@@ -6,315 +8,118 @@ require 'socket'
6
8
  require 'securerandom'
7
9
  require 'uri'
8
10
 
9
- require 'tcell_agent/config/unknown_options'
10
-
11
11
  module TCellAgent
12
12
  class ConfigurationException < StandardError
13
13
  end
14
14
 
15
15
  class << self
16
16
  attr_accessor :configuration
17
+ attr_accessor :initializer_configuration
17
18
  end
18
19
 
19
20
  def self.configure
20
- self.configuration ||= Configuration.new
21
- yield(configuration)
22
- end
23
-
24
- class Configuration # rubocop:disable Metrics/ClassLength
25
- attr_accessor :version,
26
- :app_id,
27
- :api_key,
28
- :hmac_key,
29
- :tcell_api_url,
30
- :tcell_input_url,
31
- :logging_options,
32
- :fetch_policies_from_tcell, :instrument_for_events,
33
- :preload_policy_filename,
34
- :host_identifier,
35
- :uuid,
36
- :event_batch_size_limit, :event_time_limit_seconds,
37
- :base_dir,
38
- :cache_filename,
39
- :cache_folder,
40
- :js_agent_api_base_url,
41
- :js_agent_url,
42
- :config_filename,
43
- :agent_log_dir,
44
- :max_data_ex_db_records_per_request,
45
- :agent_home_dir,
46
- :reverse_proxy,
47
- :reverse_proxy_ip_address_header,
48
- :log_file_name,
49
- :log_tag,
50
- :max_csp_header_bytes,
51
- :demomode,
52
- :allow_payloads,
53
- :password_hmac_key,
54
- :stdout_logger
55
-
56
- attr_accessor :disable_all,
57
- :enabled,
58
- :enable_event_manager, # false = Do not start the even manager
59
- :enable_event_consumer, # false = Do not consume events, drop them
60
- :enable_policy_polling, # false = Do not poll for policies
61
- :enable_instrumentation, # false = Do not add instrumentation
62
- :enable_intercept_requests # false = Do not insert middleware
21
+ require 'tcell_agent/config_initializer'
22
+ self.initializer_configuration ||= ConfigInitializer.new
63
23
 
64
- attr_accessor :enabled_instrumentations
24
+ yield(initializer_configuration)
25
+ rescue NoMethodError => e
26
+ logger = TCellAgent::ModuleLogger.new(TCellAgent::RubyLogger.new, name)
27
+ logger.error("Error configuring tcell_agent with initializers: #{e}")
28
+ end
65
29
 
66
- attr_accessor :exp_config_settings
30
+ class Configuration
31
+ # internal Ruby configurations
32
+ attr_accessor :disable_all
67
33
 
68
- attr_accessor :disable_cmdi_exec_instrumentation # true = disable cmdi Kernel::exec instrumentation
34
+ # Common config returned by libtcellagent
35
+ attr_accessor :api_key, :app_id, :disabled_instrumentation, :enabled,
36
+ :enable_intercept_requests, :fetch_policies_from_tcell, :hmac_key,
37
+ :host_identifier, :instrument, :log_dir, :logging_options,
38
+ :password_hmac_key, :reverse_proxy, :reverse_proxy_ip_address_header,
39
+ :tcell_api_url
69
40
 
70
- def should_start_event_manager?
71
- @enabled && @enable_event_manager
72
- end
41
+ # Ruby config returned by libtcellagent
42
+ attr_accessor :enable_policy_polling
73
43
 
74
44
  def should_start_policy_poll?
75
45
  @enabled && @enable_policy_polling && @fetch_policies_from_tcell # fetch_policies_from_tcel = legacy
76
46
  end
77
47
 
78
- def should_instrument?
79
- @enabled && @enable_instrumentation && @instrument_for_events # instrument_for_events = legacy
80
- end
48
+ def should_instrument?(func = nil)
49
+ return false unless @enabled && @instrument # never instrument if disabled
50
+ return true if func.nil? # always instrument if enabled and nothing given
81
51
 
82
- def should_intercept_requests?
83
- @enabled && @enable_instrumentation && @enable_intercept_requests
52
+ !@disabled_instrumentation.include?(func)
84
53
  end
85
54
 
86
- def should_instrument_doorkeeper?
87
- if @enabled_instrumentations.key?('doorkeeper') || @enabled_instrumentations.key?(:doorkeeper)
88
- !!(@enabled_instrumentations['doorkeeper'] || @enabled_instrumentations[:doorkeeper]) # rubocop:disable Style/DoubleNegation
89
- else
90
- true
91
- end
92
- end
93
-
94
- def should_instrument_devise?
95
- if @enabled_instrumentations.key?('devise') || @enabled_instrumentations.key?(:devise)
96
- !!(@enabled_instrumentations['devise'] || @enabled_instrumentations[:devise]) # rubocop:disable Style/DoubleNegation
97
- else
98
- true
99
- end
100
- end
101
-
102
- def should_instrument_authlogic?
103
- if @enabled_instrumentations.key?('authlogic') || @enabled_instrumentations.key?(:authlogic)
104
- !!(@enabled_instrumentations['authlogic'] || @enabled_instrumentations[:authlogic]) # rubocop:disable Style/DoubleNegation
105
- else
106
- true
107
- end
108
- end
109
-
110
- def should_instrument_cmdi_exec?
111
- !@disable_cmdi_exec_instrumentation
55
+ def should_intercept_requests?
56
+ @enabled && @enable_intercept_requests
112
57
  end
113
58
 
114
- def initialize(filename = 'config/tcell_agent.config', _useapp = nil)
115
- # These will be set when the agent starts up, to give rails initializers
116
- # a chance to run
117
- @cache_filename = nil
118
- @agent_log_dir = nil
119
- @log_tag = nil
120
-
121
- @version = 0
122
- @demomode = false
123
-
124
- @fetch_policies_from_tcell = true
125
- @instrument_for_events = true
126
-
59
+ def initialize
60
+ # ruby agent defaults
127
61
  @disable_all = false
128
- @enabled = true
129
- @enable_event_manager = true
130
- @enable_policy_polling = true
131
- @enable_instrumentation = true
132
- @enable_intercept_requests = true
133
-
134
- @enabled_instrumentations = {
135
- :doorkeeper => true,
136
- :devise => true,
137
- :authlogic => true
138
- }
139
-
140
- @disable_cmdi_exec_instrumentation = false
141
-
142
- @log_file_name = 'tcell_agent.log'
143
-
144
- @event_batch_size_limit = 50
145
- @event_time_limit_seconds = 15
146
-
147
- @max_data_ex_db_records_per_request = 1000
148
- @reverse_proxy = true
149
- @reverse_proxy_ip_address_header = 'X-Forwarded-For'
150
- @allow_payloads = true
151
-
152
- @max_csp_header_bytes = nil
153
- @password_hmac_key = nil
154
62
  @logging_options = {}
155
63
 
156
- @agent_home_dir = ENV['TCELL_AGENT_HOME'] || File.join(Dir.getwd, 'tcell')
157
- @cache_folder = File.join(@agent_home_dir, 'cache/')
158
- @agent_log_dir = File.join(@agent_home_dir, 'logs')
159
-
160
- @config_filename = ENV['TCELL_AGENT_CONFIG'] || File.join(Dir.getwd, filename)
161
-
162
- read_config_from_file(@config_filename)
163
- read_config_using_env
164
-
165
- if @demomode
166
- @event_batch_size_limit = 1
167
- @event_time_limit_seconds = 2
168
- end
169
-
170
- @tcell_api_url ||= 'https://us.agent.tcell.insight.rapid7.com/api/v1'
171
- @tcell_input_url ||= 'https://us.input.tcell.insight.rapid7.com/api/v1'
172
- @js_agent_url ||= 'https://us.jsagent.tcell.insight.rapid7.com/tcellagent.min.js'
173
-
174
- if @host_identifier.nil?
175
- begin
176
- @host_identifier = (Socket.gethostname || 'localhost')
177
- rescue StandardError
178
- @host_identifier = 'host_identifier_not_found'
179
- end
180
- end
181
-
182
- @uuid = SecureRandom.uuid
64
+ check_for_disabled_instrumentation(get_config_file_name)
183
65
  end
184
66
 
185
- def read_config_using_env
186
- @app_id = ENV['TCELL_AGENT_APP_ID'] || @app_id
187
- @api_key = ENV['TCELL_AGENT_API_KEY'] || @api_key
188
- @hmac_key = ENV['TCELL_HMAC_KEY'] || @hmac_key
189
- @password_hmac_key = ENV['TCELL_PASSWORD_HMAC_KEY'] || @password_hmac_key
190
- @host_identifier = ENV['TCELL_AGENT_HOST_IDENTIFIER'] || @host_identifier
191
- @tcell_api_url = ENV['TCELL_API_URL'] || @tcell_api_url
192
- @tcell_input_url = ENV['TCELL_INPUT_URL'] || @tcell_input_url
193
- @demomode = ENV['TCELL_DEMOMODE'] || @demomode
194
-
195
- @agent_log_dir = ENV['TCELL_AGENT_LOG_DIR'] || @agent_log_dir
196
- @log_file_name = ENV['TCELL_AGENT_LOG_FILENAME'] || @log_file_name
67
+ def get_config_file_dir
68
+ return nil unless ENV['TCELL_AGENT_HOME']
69
+ return nil if ENV['TCELL_AGENT_CONFIG']
197
70
 
198
- @logging_options['enabled'] = to_bool(ENV['TCELL_AGENT_LOG_ENABLED']) unless to_bool(ENV['TCELL_AGENT_LOG_ENABLED']).nil?
199
- @logging_options['level'] = ENV['TCELL_AGENT_LOG_LEVEL'] || @logging_options['level'] unless @logging_options.nil?
200
-
201
- @enabled = to_bool(ENV['TCELL_AGENT_ENABLED']) unless to_bool(ENV['TCELL_AGENT_ENABLED']).nil?
71
+ File.join(Dir.getwd, '/config')
72
+ end
202
73
 
203
- @allow_payloads = to_bool(ENV['TCELL_AGENT_ALLOW_PAYLOADS']) unless to_bool(ENV['TCELL_AGENT_ALLOW_PAYLOADS']).nil?
204
- @disable_cmdi_exec_instrumentation = to_bool(ENV['TCELL_CMDI_EXEC_DISABLED']) || @disable_cmdi_exec_instrumentation
74
+ def get_config_file_name
75
+ ENV['TCELL_AGENT_CONFIG'] || File.join(Dir.getwd, '/config/tcell_agent.config')
205
76
  end
206
77
 
207
- def read_config_from_file(filename)
78
+ def check_for_disabled_instrumentation(filename)
208
79
  return unless File.file?(filename)
209
80
 
210
81
  begin
211
- config_text = File.open(filename).read
212
- config = JSON.parse(config_text)
213
-
214
- messages = TCellAgent::Config::Validate.get_unknown_options(config)
215
- messages.each do |message|
216
- puts message
217
- end
82
+ config = JSON.parse(File.open(filename).read)
218
83
 
219
84
  if config['version'] == 1
220
- # Required
221
- app_data = config['applications'][0] # Default
222
- @version = 1
223
- @app_id = app_data['app_id']
224
- @api_key = app_data['api_key']
225
-
226
- # Optional
227
- @preload_policy_filename = app_data.fetch('preload_policy_filename', nil)
228
-
229
- @disable_all = app_data.fetch('disable_all', @disable_all)
230
- @enabled = app_data.fetch('enabled', @enabled)
231
-
232
- @enable_event_manager = app_data.fetch('enable_event_manager', @enable_event_manager)
233
- @enable_policy_polling = app_data.fetch('enable_policy_polling', @enable_policy_polling)
234
- @enable_instrumentation = app_data.fetch('enable_instrumentation', @enable_instrumentation)
235
- @enable_intercept_requests = app_data.fetch('enable_intercept_requests', @enable_intercept_requests)
236
- @fetch_policies_from_tcell = app_data.fetch('fetch_policies_from_tcell', @fetch_policies_from_tcell)
237
- @instrument_for_events = app_data.fetch('instrument_for_events', @instrument_for_events)
238
-
239
- @logging_options = app_data.fetch('logging_options', {})
240
- @agent_log_dir = app_data.fetch('log_dir', @agent_log_dir)
241
- @log_file_name = @logging_options['filename'] || @log_file_name
242
-
243
- @tcell_api_url = app_data.fetch('tcell_api_url', @tcell_api_url)
244
- @tcell_input_url = app_data.fetch('tcell_input_url', @tcell_input_url)
245
-
246
- @max_csp_header_bytes = app_data.fetch('max_csp_header_bytes', @max_csp_header_bytes)
247
-
248
- @allow_payloads = app_data.fetch(
249
- 'allow_payloads',
250
- @allow_payloads
251
- )
252
-
253
- data_exposure = app_data.fetch('data_exposure', {})
254
- @max_data_ex_db_records_per_request = data_exposure.fetch('max_data_ex_db_records_per_request', @max_data_ex_db_records_per_request)
255
-
256
- @enabled_instrumentations = app_data.fetch('enabled_instrumentations', @enabled_instrumentations)
257
-
258
- @reverse_proxy = app_data.fetch('reverse_proxy', @reverse_proxy)
259
- @reverse_proxy_ip_address_header = app_data.fetch('reverse_proxy_ip_address_header', @reverse_proxy_ip_address_header)
260
-
261
- @host_identifier = app_data.fetch('host_identifier', @host_identifier)
262
- @hmac_key = app_data.fetch('hmac_key', @hmac_key)
263
-
264
- @password_hmac_key = app_data.fetch('password_hmac_key', @password_hmac_key)
265
-
266
- @uuid = SecureRandom.uuid
267
- @uuid = 'secure-random-failed' if @uuid.nil?
268
-
269
- if app_data.key?('js_agent_api_base_url')
270
- @js_agent_api_base_url = app_data['js_agent_api_base_url']
271
- end
272
- if app_data.key?('js_agent_url')
273
- @js_agent_url = app_data['js_agent_url']
274
- end
275
-
276
- @demomode = app_data.fetch('demomode', @demomode)
277
- else
278
- puts ' ********* ********* ********* *********'
279
- puts '* tCell.io *'
280
- puts '* Unsupported config file version *'
281
- puts ' ********* ********* ********* *********'
85
+ app_data = config['applications'][0]
86
+ @disable_all = to_bool(app_data.fetch('disable_all', @disable_all))
282
87
  end
283
- rescue StandardError => e
284
- puts ' ********* ********* ********* *********'
285
- puts '* tCell.io *'
286
- puts '* Could not load config file *'
287
- puts ' ********* ********* ********* *********'
288
- puts e
88
+ rescue StandardError # rubocop:disable Lint/HandleExceptions
289
89
  end
290
90
  end
291
91
 
292
92
  def to_bool(var)
293
- return unless var
294
- var.to_s.casecmp('true').zero? if %w[true false].include? var.to_s.downcase
295
- end
296
-
297
- def enforce_symbol_keys(hashmap)
298
- hashmap.each_with_object({}) do |(k, v), memo|
299
- memo[k.to_sym] = v
300
- end
301
- end
302
-
303
- def logging_enabled?
304
- @enabled && enforce_symbol_keys(@logging_options || {})[:enabled]
305
- end
306
-
307
- def log_filename
308
- @agent_log_dir ||= File.join(@agent_home_dir, 'logs')
309
- File.join(@agent_log_dir, @log_file_name)
310
- end
311
-
312
- def clean_logging_options
313
- {
314
- :enabled => true,
315
- :level => 'INFO',
316
- :filename => log_file_name
317
- }.merge(enforce_symbol_keys(@logging_options || {}))
93
+ { 'true' => true, 'false' => false }[var.to_s.downcase]
94
+ end
95
+
96
+ def populate_configuration(native_agent_config_response)
97
+ # config
98
+ @enabled = native_agent_config_response['enabled']
99
+ @disabled_instrumentation = Set.new(native_agent_config_response['disabled_instrumentation'])
100
+ @fetch_policies_from_tcell = native_agent_config_response['update_policy']
101
+ @instrument = native_agent_config_response['instrument']
102
+
103
+ # app config
104
+ apps = native_agent_config_response['applications'] ? native_agent_config_response['applications']['first'] : {}
105
+ @app_id = apps['app_id']
106
+ @api_key = apps['api_key']
107
+ @hmac_key = apps['hmac_key']
108
+ @password_hmac_key = apps['password_hmac_key']
109
+
110
+ # proxy config
111
+ proxy_config = apps['proxy_config'] || {}
112
+ @reverse_proxy = proxy_config['reverse_proxy']
113
+ @reverse_proxy_ip_address_header = proxy_config['reverse_proxy_ip_address_header']
114
+
115
+ # endpoint config
116
+ endpoint = native_agent_config_response['endpoint_config'] || {}
117
+ @tcell_api_url = endpoint['api_url']
118
+
119
+ # ruby config
120
+ ruby_config = native_agent_config_response['ruby_config'] || {}
121
+ @enable_policy_polling = ruby_config['enable_policy_polling']
122
+ @enable_intercept_requests = !@disabled_instrumentation.include?('intercept_requests')
318
123
  end
319
124
  end
320
125