tcell_agent 0.2.2 → 0.2.4
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.
- checksums.yaml +4 -4
- data/bin/tcell_agent +97 -32
- data/lib/tcell_agent.rb +4 -4
- data/lib/tcell_agent/agent.rb +68 -13
- data/lib/tcell_agent/agent/event_processor.rb +256 -79
- data/lib/tcell_agent/agent/fork_pipe_manager.rb +114 -0
- data/lib/tcell_agent/agent/policy_manager.rb +28 -16
- data/lib/tcell_agent/agent/policy_types.rb +3 -4
- data/lib/tcell_agent/agent/route_manager.rb +45 -0
- data/lib/tcell_agent/agent/static_agent.rb +48 -10
- data/lib/tcell_agent/api.rb +0 -2
- data/lib/tcell_agent/appsensor/path_traversal.rb +1 -1
- data/lib/tcell_agent/configuration.rb +19 -6
- data/lib/tcell_agent/instrumentation.rb +123 -0
- data/lib/tcell_agent/logger.rb +4 -1
- data/lib/tcell_agent/policies/content_security_policy.rb +44 -8
- data/lib/tcell_agent/policies/dataloss_policy.rb +122 -65
- data/lib/tcell_agent/rails.rb +19 -10
- data/{config/initializers/authlogic_auth.rb → lib/tcell_agent/rails/auth/authlogic.rb} +1 -0
- data/{config/initializers/devise_auth.rb → lib/tcell_agent/rails/auth/devise.rb} +2 -81
- data/lib/tcell_agent/rails/dlp.rb +40 -36
- data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +2 -2
- data/lib/tcell_agent/rails/middleware/headers_middleware.rb +8 -2
- data/lib/tcell_agent/rails/routes.rb +15 -19
- data/lib/tcell_agent/routes/table.rb +35 -0
- data/lib/tcell_agent/sensor_events/app_sensor.rb +22 -33
- data/lib/tcell_agent/sensor_events/discovery.rb +30 -0
- data/lib/tcell_agent/sensor_events/dlp.rb +9 -4
- data/lib/tcell_agent/sensor_events/sensor.rb +3 -0
- data/lib/tcell_agent/sensor_events/server_agent.rb +30 -6
- data/lib/tcell_agent/sensor_events/util/utils.rb +5 -1
- data/lib/tcell_agent/start_background_thread.rb +27 -22
- data/lib/tcell_agent/utils/queue_with_timeout.rb +65 -1
- data/lib/tcell_agent/version.rb +1 -1
- data/spec/lib/tcell_agent/instrumentation_spec.rb +198 -0
- data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +37 -2
- data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +81 -8
- data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +3 -3
- data/spec/spec_helper.rb +16 -0
- metadata +11 -11
- data/config/initializers/init.rb +0 -8
- data/lib/tcell_agent/dataloss.rb +0 -0
- data/lib/tcell_agent/policies/add_script_tag_policy.rb +0 -47
- data/lib/tcell_agent/rails/devise.rb +0 -0
- data/spec/lib/tcell_agent/policies/add_script_tag_policy_spec.rb +0 -37
@@ -2,6 +2,41 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module TCellAgent
|
4
4
|
module Policies
|
5
|
+
describe ContentSecurityPolicy do
|
6
|
+
policy_json_empty = {
|
7
|
+
"policy_id"=>"01a1",
|
8
|
+
"data"=>{
|
9
|
+
"options"=>{
|
10
|
+
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
policy_json_one = {
|
16
|
+
"policy_id"=>"01a1",
|
17
|
+
"data"=>{
|
18
|
+
"options"=>{
|
19
|
+
"js_agent_api_key"=>"000-000-1"
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
empty_policy = ContentSecurityPolicy.fromJson(policy_json_empty)
|
25
|
+
context "test empty agent" do
|
26
|
+
it "enabled is false" do
|
27
|
+
expect(empty_policy.policy_id).to eq("01a1")
|
28
|
+
expect(empty_policy.js_agent_api_key).to eq(nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
from_json = ContentSecurityPolicy.fromJson(policy_json_one)
|
33
|
+
context "tests xss is true and enabled true" do
|
34
|
+
it "returns true" do
|
35
|
+
expect(from_json.policy_id).to eq("01a1")
|
36
|
+
expect(from_json.js_agent_api_key).to eq("000-000-1")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
5
40
|
describe ContentSecurityPolicy do
|
6
41
|
content_security_policy_json = {
|
7
42
|
"policy_id"=>"00a1",
|
@@ -62,8 +97,8 @@ module TCellAgent
|
|
62
97
|
context "secure header, report-uri seperate" do
|
63
98
|
it "returns false" do
|
64
99
|
expect(csp_policy.headers.length).to eq(1)
|
65
|
-
expect(csp_policy.headers[0].value).to eq("value normal; report-uri https://example.com/abcdde")
|
66
|
-
expect(csp_policy.headers[0].value("1","2","3")).to eq("value normal; report-uri https://example.com/abcdde?tid=1&sid=2&
|
100
|
+
expect(csp_policy.headers[0].value).to eq("value normal; report-uri https://example.com/abcdde?c=-815891691")
|
101
|
+
expect(csp_policy.headers[0].value("1","2","3")).to eq("value normal; report-uri https://example.com/abcdde?tid=1&sid=3&rid=2&c=1777384531")
|
67
102
|
end
|
68
103
|
end
|
69
104
|
end
|
@@ -19,11 +19,11 @@ module TCellAgent
|
|
19
19
|
}
|
20
20
|
}
|
21
21
|
policy = DataLossPolicy.fromJson(policy_json)
|
22
|
-
context "initialized with 3 items" do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
22
|
+
# context "initialized with 3 items" do
|
23
|
+
# it "returns true" do
|
24
|
+
# expect(policy.get_actions_for("user","ssn")).to eq(["body_redact"].to_set)
|
25
|
+
# end
|
26
|
+
# end
|
27
27
|
policy_json_two = {
|
28
28
|
"policy_id"=>"x1a1",
|
29
29
|
"data"=>{
|
@@ -38,6 +38,80 @@ module TCellAgent
|
|
38
38
|
expect(policy_two.get_actions_for_session_id.log_event).to eq(true)
|
39
39
|
end
|
40
40
|
end
|
41
|
+
context "Database Options" do
|
42
|
+
it "Ignores bad table" do
|
43
|
+
policy_json_requests = {
|
44
|
+
"policy_id"=>"x1a1",
|
45
|
+
"data"=>{
|
46
|
+
"db_protections"=>[
|
47
|
+
{
|
48
|
+
"databases"=>["dave"],
|
49
|
+
"schemas"=>["sam"],
|
50
|
+
"tables"=>["trevor"],
|
51
|
+
"fields"=>["fred"],
|
52
|
+
"actions"=>{
|
53
|
+
"log"=>["redact"],
|
54
|
+
"body"=>["event"]
|
55
|
+
}
|
56
|
+
}
|
57
|
+
]
|
58
|
+
}
|
59
|
+
}
|
60
|
+
db_one_policy = DataLossPolicy.fromJson(policy_json_requests)
|
61
|
+
expect(db_one_policy.get_actions_for_table("dave","sam","tommy","fred")).to eq(nil)
|
62
|
+
end
|
63
|
+
it "Partial Policy" do
|
64
|
+
# Assume if databases, schemas that are missing are "*"
|
65
|
+
policy_json_requests = {
|
66
|
+
"policy_id"=>"x1a1",
|
67
|
+
"data"=>{
|
68
|
+
"db_protections"=>[
|
69
|
+
{
|
70
|
+
"fields"=>["fred"],
|
71
|
+
"actions"=>{
|
72
|
+
"log"=>["redact"],
|
73
|
+
"body"=>["event"]
|
74
|
+
}
|
75
|
+
}
|
76
|
+
]
|
77
|
+
}
|
78
|
+
}
|
79
|
+
db_one_policy = DataLossPolicy.fromJson(policy_json_requests)
|
80
|
+
expect((db_one_policy.get_actions_for_table("dave","sam","tommy","fred").to_a)[0].log_redact).to eq(true)
|
81
|
+
expect((db_one_policy.get_actions_for_table("dave","sam","tommy","fred","abcd").to_a)[0].log_redact).to eq(true)
|
82
|
+
end
|
83
|
+
it "Scopes by reoute_id" do
|
84
|
+
policy_json_request_ids = {
|
85
|
+
"policy_id"=>"x1a1",
|
86
|
+
"data"=>{
|
87
|
+
"db_protections"=>[
|
88
|
+
{
|
89
|
+
"scope"=>"route",
|
90
|
+
"route_ids"=>["abcd"],
|
91
|
+
"databases"=>["dave"],
|
92
|
+
"schemas"=>["sam"],
|
93
|
+
"tables"=>["tommy"],
|
94
|
+
"fields"=>["fred"],
|
95
|
+
"actions"=>{
|
96
|
+
"log"=>["redact"],
|
97
|
+
"body"=>["event"]
|
98
|
+
}
|
99
|
+
}
|
100
|
+
]
|
101
|
+
}
|
102
|
+
}
|
103
|
+
db_two_policy = DataLossPolicy.fromJson(policy_json_request_ids)
|
104
|
+
expect((db_two_policy.get_actions_for_table("dave","sam","tommy","fred").to_a).size).to eq(0)
|
105
|
+
expect((db_two_policy.get_actions_for_table("dave","sam","tommy","fred","other_route").to_a).size).to eq(0)
|
106
|
+
expect((db_two_policy.get_actions_for_table("dave","sam","tommy","fred","abcd").to_a).size).to eq(1)
|
107
|
+
|
108
|
+
|
109
|
+
expect((db_two_policy.get_actions_for_table("dave","sam","tommy","fred","abcd").to_a)[0].log_redact).to eq(true)
|
110
|
+
expect((db_two_policy.get_actions_for_table("dave","sam","tommy","fred","abcd").to_a)[0].body_redact).to eq(nil)
|
111
|
+
expect((db_two_policy.get_actions_for_table("dave","sam","tommy","fred","abcd").to_a)[0].body_event).to eq(true)
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
41
115
|
context "Request Options" do
|
42
116
|
it "Ignores non-global scoped policy" do
|
43
117
|
policy_json_requests = {
|
@@ -48,7 +122,7 @@ module TCellAgent
|
|
48
122
|
"context"=>"form",
|
49
123
|
"scope"=>"route",
|
50
124
|
"variable"=>"test123",
|
51
|
-
"
|
125
|
+
"actions"=>{
|
52
126
|
"log"=>["redact"],
|
53
127
|
"body"=>["event"]
|
54
128
|
}
|
@@ -67,7 +141,7 @@ module TCellAgent
|
|
67
141
|
{
|
68
142
|
"context"=>"form",
|
69
143
|
"variable"=>"test123",
|
70
|
-
"
|
144
|
+
"actions"=>{
|
71
145
|
"log"=>["redact"],
|
72
146
|
"body"=>["event"]
|
73
147
|
}
|
@@ -76,7 +150,6 @@ module TCellAgent
|
|
76
150
|
}
|
77
151
|
}
|
78
152
|
policy_three = DataLossPolicy.fromJson(policy_json_requests)
|
79
|
-
puts policy_three.request_filter_actions
|
80
153
|
expect(policy_three.get_actions_for_request("form").keys).to eq(["test123"])
|
81
154
|
expect(policy_three.get_actions_for_request("form")["test123"].log_redact).to eq(true)
|
82
155
|
expect(policy_three.get_actions_for_request("form")["test123"].body_redact).to eq(nil)
|
@@ -23,6 +23,8 @@ module TCellAgent
|
|
23
23
|
if (rack_request.params['rv'])
|
24
24
|
response_headers["Location"] = rack_request.params['rv']
|
25
25
|
end
|
26
|
+
env["tcell.request_data"].transaction_id = "a-b-c-d-e-f"
|
27
|
+
#env["tcell.request_data"].route_id = "x-b-c-d-e-f"
|
26
28
|
[200, response_headers, ['OK']]
|
27
29
|
end
|
28
30
|
|
@@ -90,8 +92,6 @@ module TCellAgent
|
|
90
92
|
"whitelist"=>["*.google.com"]
|
91
93
|
}
|
92
94
|
}}, cache=false)
|
93
|
-
|
94
|
-
tid_len = "78e596b7-e772-4caf-92eb-645fdbdec473".length + 1
|
95
95
|
response = request.get("/some/path?rv=https://www.google.com", 'CONTENT_TYPE' => 'text/html')
|
96
96
|
expect(response['Location']).to eq("https://www.google.com")
|
97
97
|
end
|
@@ -139,7 +139,7 @@ module TCellAgent
|
|
139
139
|
}}, cache=false)
|
140
140
|
tid_len = "78e596b7-e772-4caf-92eb-645fdbdec473".length + 1
|
141
141
|
response = request.get("/some/path", 'CONTENT_TYPE' => 'text/plain', 'action_dispatch.request_id'=>'35281717-247e-44e6-bd42-0fb1417e80d')
|
142
|
-
expect(response['Content-Security-Policy-Report-Only']
|
142
|
+
expect(response['Content-Security-Policy-Report-Only']).to eq("script-src 'unsafe-inline'; report-uri http://test.tcell.io/report?tid=a-b-c-d-e-f&c=-654192056")
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
data/spec/spec_helper.rb
CHANGED
@@ -2,3 +2,19 @@ require 'rspec'
|
|
2
2
|
require 'rails'
|
3
3
|
require 'webmock/rspec'
|
4
4
|
require File.join(File.dirname(__FILE__), '..', 'lib', 'tcell_agent')
|
5
|
+
|
6
|
+
module TCellAgent
|
7
|
+
@@spec_event_queue = []
|
8
|
+
def self.empty_event_queue
|
9
|
+
@@spec_event_queue = []
|
10
|
+
end
|
11
|
+
def self.event_queue
|
12
|
+
return @@spec_event_queue
|
13
|
+
end
|
14
|
+
def self.send_event(event)
|
15
|
+
@@spec_event_queue.push(event)
|
16
|
+
end
|
17
|
+
def self.set_thread_agent(thread_agent)
|
18
|
+
self.thread_agent = thread_agent
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tcell_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garrett
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -121,14 +121,13 @@ files:
|
|
121
121
|
- README.md
|
122
122
|
- Rakefile
|
123
123
|
- bin/tcell_agent
|
124
|
-
- config/initializers/authlogic_auth.rb
|
125
|
-
- config/initializers/devise_auth.rb
|
126
|
-
- config/initializers/init.rb
|
127
124
|
- lib/tcell_agent.rb
|
128
125
|
- lib/tcell_agent/agent.rb
|
129
126
|
- lib/tcell_agent/agent/event_processor.rb
|
127
|
+
- lib/tcell_agent/agent/fork_pipe_manager.rb
|
130
128
|
- lib/tcell_agent/agent/policy_manager.rb
|
131
129
|
- lib/tcell_agent/agent/policy_types.rb
|
130
|
+
- lib/tcell_agent/agent/route_manager.rb
|
132
131
|
- lib/tcell_agent/agent/static_agent.rb
|
133
132
|
- lib/tcell_agent/api.rb
|
134
133
|
- lib/tcell_agent/appsensor.rb
|
@@ -138,11 +137,9 @@ files:
|
|
138
137
|
- lib/tcell_agent/appsensor/xss.rb
|
139
138
|
- lib/tcell_agent/authlogic.rb
|
140
139
|
- lib/tcell_agent/configuration.rb
|
141
|
-
- lib/tcell_agent/dataloss.rb
|
142
140
|
- lib/tcell_agent/devise.rb
|
143
141
|
- lib/tcell_agent/instrumentation.rb
|
144
142
|
- lib/tcell_agent/logger.rb
|
145
|
-
- lib/tcell_agent/policies/add_script_tag_policy.rb
|
146
143
|
- lib/tcell_agent/policies/appsensor_policy.rb
|
147
144
|
- lib/tcell_agent/policies/clickjacking_policy.rb
|
148
145
|
- lib/tcell_agent/policies/content_security_policy.rb
|
@@ -153,7 +150,8 @@ files:
|
|
153
150
|
- lib/tcell_agent/policies/login_fraud_policy.rb
|
154
151
|
- lib/tcell_agent/policies/secure_headers_policy.rb
|
155
152
|
- lib/tcell_agent/rails.rb
|
156
|
-
- lib/tcell_agent/rails/
|
153
|
+
- lib/tcell_agent/rails/auth/authlogic.rb
|
154
|
+
- lib/tcell_agent/rails/auth/devise.rb
|
157
155
|
- lib/tcell_agent/rails/dlp.rb
|
158
156
|
- lib/tcell_agent/rails/middleware/body_filter_middleware.rb
|
159
157
|
- lib/tcell_agent/rails/middleware/context_middleware.rb
|
@@ -161,8 +159,10 @@ files:
|
|
161
159
|
- lib/tcell_agent/rails/middleware/headers_middleware.rb
|
162
160
|
- lib/tcell_agent/rails/routes.rb
|
163
161
|
- lib/tcell_agent/rails/settings_reporter.rb
|
162
|
+
- lib/tcell_agent/routes/table.rb
|
164
163
|
- lib/tcell_agent/sensor_events/app_config.rb
|
165
164
|
- lib/tcell_agent/sensor_events/app_sensor.rb
|
165
|
+
- lib/tcell_agent/sensor_events/discovery.rb
|
166
166
|
- lib/tcell_agent/sensor_events/dlp.rb
|
167
167
|
- lib/tcell_agent/sensor_events/honeytokens.rb
|
168
168
|
- lib/tcell_agent/sensor_events/login_fraud.rb
|
@@ -180,7 +180,7 @@ files:
|
|
180
180
|
- spec/controllers/application_controller.rb
|
181
181
|
- spec/lib/tcell_agent/api/api_spec.rb
|
182
182
|
- spec/lib/tcell_agent/appsensor_spec.rb
|
183
|
-
- spec/lib/tcell_agent/
|
183
|
+
- spec/lib/tcell_agent/instrumentation_spec.rb
|
184
184
|
- spec/lib/tcell_agent/policies/appsensor_policy_spec.rb
|
185
185
|
- spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb
|
186
186
|
- spec/lib/tcell_agent/policies/content_security_policy_spec.rb
|
@@ -221,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
221
221
|
version: '0'
|
222
222
|
requirements: []
|
223
223
|
rubyforge_project:
|
224
|
-
rubygems_version: 2.
|
224
|
+
rubygems_version: 2.4.8
|
225
225
|
signing_key:
|
226
226
|
specification_version: 4
|
227
227
|
summary: tCell.io Agent for Rails & Sinatra
|
@@ -229,7 +229,7 @@ test_files:
|
|
229
229
|
- spec/controllers/application_controller.rb
|
230
230
|
- spec/lib/tcell_agent/api/api_spec.rb
|
231
231
|
- spec/lib/tcell_agent/appsensor_spec.rb
|
232
|
-
- spec/lib/tcell_agent/
|
232
|
+
- spec/lib/tcell_agent/instrumentation_spec.rb
|
233
233
|
- spec/lib/tcell_agent/policies/appsensor_policy_spec.rb
|
234
234
|
- spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb
|
235
235
|
- spec/lib/tcell_agent/policies/content_security_policy_spec.rb
|
data/config/initializers/init.rb
DELETED
data/lib/tcell_agent/dataloss.rb
DELETED
File without changes
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'tcell_agent/configuration'
|
2
|
-
|
3
|
-
module TCellAgent
|
4
|
-
module Policies
|
5
|
-
class AddScriptTagPolicy
|
6
|
-
attr_accessor :enabled
|
7
|
-
attr_accessor :policy_id
|
8
|
-
attr_accessor :js_agent_api_key
|
9
|
-
def initialize
|
10
|
-
self.init_options
|
11
|
-
end
|
12
|
-
def init_options
|
13
|
-
@enabled = false
|
14
|
-
@policy_id = nil
|
15
|
-
@js_agent_api_key = nil
|
16
|
-
end
|
17
|
-
def js_agent_app_id
|
18
|
-
return TCellAgent.configuration.app_id
|
19
|
-
end
|
20
|
-
def js_agent_api_base_url
|
21
|
-
return TCellAgent.configuration.js_agent_api_base_url
|
22
|
-
end
|
23
|
-
def js_agent_url
|
24
|
-
return TCellAgent.configuration.js_agent_url
|
25
|
-
end
|
26
|
-
def self.fromJson(policy_json)
|
27
|
-
if (!policy_json)
|
28
|
-
return nil
|
29
|
-
end
|
30
|
-
policy = AddScriptTagPolicy.new
|
31
|
-
if policy_json.has_key?("policy_id")
|
32
|
-
policy.policy_id = policy_json["policy_id"]
|
33
|
-
else
|
34
|
-
raise "Policy ID missing"
|
35
|
-
end
|
36
|
-
if policy_json.has_key?("data")
|
37
|
-
data_json = policy_json["data"]
|
38
|
-
policy.js_agent_api_key = data_json.fetch("js_agent_api_key", nil)
|
39
|
-
if policy.js_agent_api_key != nil
|
40
|
-
policy.enabled = true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
return policy
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
File without changes
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module TCellAgent
|
4
|
-
module Policies
|
5
|
-
describe AddScriptTagPolicy do
|
6
|
-
policy_json_empty = {
|
7
|
-
"policy_id"=>"01a1",
|
8
|
-
"data"=>{
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
|
-
policy_json_one = {
|
13
|
-
"policy_id"=>"01a1",
|
14
|
-
"data"=>{
|
15
|
-
"js_agent_api_key"=>"000-000-1"
|
16
|
-
}
|
17
|
-
}
|
18
|
-
|
19
|
-
empty_policy = AddScriptTagPolicy.fromJson(policy_json_empty)
|
20
|
-
context "test empty agent" do
|
21
|
-
it "enabled is false" do
|
22
|
-
expect(empty_policy.policy_id).to eq("01a1")
|
23
|
-
expect(empty_policy.enabled).to eq(false)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
from_json = AddScriptTagPolicy.fromJson(policy_json_one)
|
28
|
-
context "tests xss is true and enabled true" do
|
29
|
-
it "returns true" do
|
30
|
-
expect(from_json.policy_id).to eq("01a1")
|
31
|
-
expect(from_json.enabled).to eq(true)
|
32
|
-
expect(from_json.js_agent_api_key).to eq("000-000-1")
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|