tcell_agent 0.2.7 → 0.2.8
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 +22 -0
- data/lib/tcell_agent/agent/event_processor.rb +7 -0
- data/lib/tcell_agent/agent/fork_pipe_manager.rb +29 -29
- data/lib/tcell_agent/agent/policy_manager.rb +2 -1
- data/lib/tcell_agent/agent/route_manager.rb +35 -15
- data/lib/tcell_agent/configuration.rb +42 -2
- data/lib/tcell_agent/instrumentation.rb +4 -1
- data/lib/tcell_agent/logger.rb +1 -1
- data/lib/tcell_agent/rails.rb +12 -18
- data/lib/tcell_agent/rails/auth/authlogic.rb +2 -2
- data/lib/tcell_agent/rails/auth/devise.rb +1 -1
- data/lib/tcell_agent/rails/dlp.rb +133 -123
- data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +2 -1
- data/lib/tcell_agent/rails/on_start.rb +67 -69
- data/lib/tcell_agent/rails/routes.rb +91 -86
- data/lib/tcell_agent/rails/settings_reporter.rb +10 -0
- data/lib/tcell_agent/routes/table.rb +2 -0
- data/lib/tcell_agent/sensor_events/server_agent.rb +10 -0
- data/lib/tcell_agent/servers/thin.rb +1 -0
- data/lib/tcell_agent/servers/webrick.rb +0 -1
- data/lib/tcell_agent/start_background_thread.rb +44 -45
- data/lib/tcell_agent/system_info.rb +10 -0
- data/lib/tcell_agent/version.rb +1 -1
- data/spec/lib/tcell_agent/agent/fork_pipe_manager_spec.rb +99 -0
- data/spec/lib/tcell_agent/api/api_spec.rb +2 -2
- data/spec/lib/tcell_agent/instrumentation_spec.rb +176 -176
- data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +32 -32
- data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +63 -63
- data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +93 -93
- data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +222 -222
- data/spec/lib/tcell_agent/policies/honeytokens_policy_spec.rb +17 -17
- data/spec/lib/tcell_agent/policies/http_redirect_policy_spec.rb +57 -57
- data/spec/lib/tcell_agent/policies/http_tx_policy_spec.rb +17 -17
- data/spec/lib/tcell_agent/policies/login_policy_spec.rb +3 -3
- data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +59 -59
- data/spec/lib/tcell_agent/rails/logger_spec.rb +148 -0
- data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +7 -7
- data/spec/lib/tcell_agent/rails_spec.rb +2 -2
- data/spec/lib/tcell_agent/sensor_events/dlp_spec.rb +9 -9
- data/spec/lib/tcell_agent/sensor_events/util/redirect_utils_spec.rb +20 -20
- data/spec/lib/tcell_agent/sensor_events/util/sanitizer_utilities_spec.rb +52 -52
- data/spec/lib/tcell_agent_spec.rb +17 -17
- data/spec/spec_helper.rb +1 -0
- data/spec/support/resources/normal_config.json +5 -5
- data/tcell_agent.gemspec +4 -4
- metadata +31 -26
@@ -1,22 +1,22 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module TCellAgent
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
4
|
+
module Policies
|
5
|
+
describe HoneytokensPolicy do
|
6
|
+
policy_json = {
|
7
|
+
"policy_id"=>"x1a1",
|
8
|
+
"token_salt"=>"saltsaltsalt",
|
9
|
+
"tokens"=>[
|
10
|
+
{"type"=>"cred", "token"=>"TOKEN", "id"=>"ID001"}
|
11
|
+
]
|
12
|
+
}
|
13
|
+
policy = HoneytokensPolicy.fromJson(policy_json)
|
14
|
+
context "initialized with 3 items" do
|
15
|
+
it "returns true" do
|
16
|
+
expect(policy.policy_id).to eq("x1a1")
|
17
|
+
expect(policy.cred_tokens["TOKEN"]).to eq("ID001")
|
20
18
|
end
|
19
|
+
end
|
21
20
|
end
|
22
|
-
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,62 +1,62 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module TCellAgent
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
end
|
13
|
-
http_redirect_policy_json = {
|
14
|
-
"policy_id"=>"x1a1",
|
15
|
-
"data"=>{
|
16
|
-
"enabled"=>true
|
17
|
-
}
|
18
|
-
}
|
19
|
-
http_redirect_from_json = HttpRedirectPolicy.fromJson(http_redirect_policy_json)
|
20
|
-
context "initialized with 3 items" do
|
21
|
-
it "returns true" do
|
22
|
-
expect(http_redirect_from_json.policy_id).to eq("x1a1")
|
23
|
-
expect(http_redirect_from_json.enabled).to eq(true)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
context "check url" do
|
27
|
-
it "see's other domain" do
|
28
|
-
result = http_redirect_from_json.check("test.google.com", "www.test.com")
|
29
|
-
expect(result).to eq(true)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
context "check url" do
|
33
|
-
it "wildcard domain false" do
|
34
|
-
http_redirect_from_json.whitelist = ["*.google.com"]
|
35
|
-
result = http_redirect_from_json.check("test.google.com", "www.test.com")
|
36
|
-
expect(result).to eq(false)
|
37
|
-
end
|
38
|
-
it "wildcard domain true" do
|
39
|
-
http_redirect_from_json.whitelist = ["*.google.com"]
|
40
|
-
result = http_redirect_from_json.check("test.google.net", "www.test.com")
|
41
|
-
expect(result).to eq(true)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
context "enforce url" do
|
45
|
-
it "domain enforce enabled false, block true" do
|
46
|
-
http_redirect_from_json.enabled = false
|
47
|
-
http_redirect_from_json.block = true
|
48
|
-
http_redirect_from_json.whitelist = ["*.google.com"]
|
49
|
-
result = http_redirect_from_json.enforce("https://test.google.com", "www.test.com", "/path/a", "GET", "1.1.1.1", 400)
|
50
|
-
expect(result).to eq(nil)
|
51
|
-
end
|
52
|
-
it "domain enforce enabled true, block true" do
|
53
|
-
http_redirect_from_json.enabled = true
|
54
|
-
http_redirect_from_json.block = true
|
55
|
-
http_redirect_from_json.whitelist = ["good.com"]
|
56
|
-
result = http_redirect_from_json.enforce("https://www.google.com/abc/def", "localhost", "/path/a", "GET", "1.1.1.1", 400)
|
57
|
-
expect(result).to eq("/")
|
58
|
-
end
|
59
|
-
end
|
4
|
+
module Policies
|
5
|
+
describe HttpRedirectPolicy do
|
6
|
+
http_redirect_plain = HttpRedirectPolicy.new
|
7
|
+
context "defaults" do
|
8
|
+
it "returns true" do
|
9
|
+
expect(http_redirect_plain.policy_id).to eq(nil)
|
10
|
+
expect(http_redirect_plain.enabled).to eq(false)
|
60
11
|
end
|
12
|
+
end
|
13
|
+
http_redirect_policy_json = {
|
14
|
+
"policy_id"=>"x1a1",
|
15
|
+
"data"=>{
|
16
|
+
"enabled"=>true
|
17
|
+
}
|
18
|
+
}
|
19
|
+
http_redirect_from_json = HttpRedirectPolicy.fromJson(http_redirect_policy_json)
|
20
|
+
context "initialized with 3 items" do
|
21
|
+
it "returns true" do
|
22
|
+
expect(http_redirect_from_json.policy_id).to eq("x1a1")
|
23
|
+
expect(http_redirect_from_json.enabled).to eq(true)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
context "check url" do
|
27
|
+
it "see's other domain" do
|
28
|
+
result = http_redirect_from_json.check("test.google.com", "www.test.com")
|
29
|
+
expect(result).to eq(true)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
context "check url" do
|
33
|
+
it "wildcard domain false" do
|
34
|
+
http_redirect_from_json.whitelist = ["*.google.com"]
|
35
|
+
result = http_redirect_from_json.check("test.google.com", "www.test.com")
|
36
|
+
expect(result).to eq(false)
|
37
|
+
end
|
38
|
+
it "wildcard domain true" do
|
39
|
+
http_redirect_from_json.whitelist = ["*.google.com"]
|
40
|
+
result = http_redirect_from_json.check("test.google.net", "www.test.com")
|
41
|
+
expect(result).to eq(true)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
context "enforce url" do
|
45
|
+
it "domain enforce enabled false, block true" do
|
46
|
+
http_redirect_from_json.enabled = false
|
47
|
+
http_redirect_from_json.block = true
|
48
|
+
http_redirect_from_json.whitelist = ["*.google.com"]
|
49
|
+
result = http_redirect_from_json.enforce("https://test.google.com", "www.test.com", "/path/a", "GET", "1.1.1.1", 400)
|
50
|
+
expect(result).to eq(nil)
|
51
|
+
end
|
52
|
+
it "domain enforce enabled true, block true" do
|
53
|
+
http_redirect_from_json.enabled = true
|
54
|
+
http_redirect_from_json.block = true
|
55
|
+
http_redirect_from_json.whitelist = ["good.com"]
|
56
|
+
result = http_redirect_from_json.enforce("https://www.google.com/abc/def", "localhost", "/path/a", "GET", "1.1.1.1", 400)
|
57
|
+
expect(result).to eq("/")
|
58
|
+
end
|
59
|
+
end
|
61
60
|
end
|
62
|
-
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,22 +1,22 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module TCellAgent
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
4
|
+
module Policies
|
5
|
+
describe HttpTxPolicy do
|
6
|
+
http_tx_policy_json = {
|
7
|
+
"policy_id"=>"01a1",
|
8
|
+
"types"=>{
|
9
|
+
"firehose"=>{"enabled"=>true}
|
10
|
+
}
|
11
|
+
}
|
12
|
+
http_tx_from_json = HttpTxPolicy.fromJson(http_tx_policy_json)
|
13
|
+
context "initialized with 3 items" do
|
14
|
+
it "returns true" do
|
15
|
+
expect(http_tx_from_json.policy_id).to eq("01a1")
|
16
|
+
expect(http_tx_from_json.firehose["enabled"]).to eq(true)
|
17
|
+
expect(http_tx_from_json.firehose["lite"]).to eq(false)
|
20
18
|
end
|
19
|
+
end
|
21
20
|
end
|
22
|
-
end
|
21
|
+
end
|
22
|
+
end
|
@@ -15,9 +15,9 @@ module TCellAgent
|
|
15
15
|
"policy_id"=>"01a1",
|
16
16
|
"data"=>{
|
17
17
|
"options"=>{
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
"login_failed_enabled"=>true,
|
19
|
+
"login_success_enabled"=>true,
|
20
|
+
"session_hijacking_enabled"=>true
|
21
21
|
}
|
22
22
|
}
|
23
23
|
}
|
@@ -1,67 +1,67 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module TCellAgent
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
4
|
+
module Policies
|
5
|
+
describe SecureHeadersPolicy do
|
6
|
+
secure_headers_policy_json = {
|
7
|
+
"policy_id"=>"01a1",
|
8
|
+
"headers"=>[
|
9
|
+
{"name"=>"x-permitted-cross-domain-policies", "value"=>"value123"}
|
10
|
+
]
|
11
|
+
}
|
12
|
+
secure_headers_policy = SecureHeadersPolicy.fromJson(secure_headers_policy_json)
|
13
|
+
context "secure header example" do
|
14
|
+
it "returns true" do
|
15
|
+
expect(secure_headers_policy.headers[0].name).to eq("x-permitted-cross-domain-policies")
|
16
|
+
expect(secure_headers_policy.headers[0].value).to eq("value123")
|
19
17
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
18
|
+
end
|
19
|
+
end
|
20
|
+
describe SecureHeadersPolicy do
|
21
|
+
secure_headers_policy_json = {
|
22
|
+
"policy_id"=>"01a1",
|
23
|
+
"headers"=>[
|
24
|
+
{"name"=>"x-frame-options", "value"=>"DENY"},
|
25
|
+
{"name"=>"x-xss-protection", "value"=>"1; mode=block"}
|
26
|
+
]
|
27
|
+
}
|
28
|
+
secure_headers_policy = SecureHeadersPolicy.fromJson(secure_headers_policy_json)
|
29
|
+
context "secure headers (2) example" do
|
30
|
+
it "returns true" do
|
31
|
+
expect(secure_headers_policy.headers[0].name).to eq("x-frame-options")
|
32
|
+
expect(secure_headers_policy.headers[0].value).to eq("DENY")
|
33
|
+
expect(secure_headers_policy.headers[1].name).to eq("x-xss-protection")
|
34
|
+
expect(secure_headers_policy.headers[1].value).to eq("1; mode=block")
|
37
35
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
36
|
+
end
|
37
|
+
end
|
38
|
+
describe SecureHeadersPolicy do
|
39
|
+
secure_headers_policy_json = {
|
40
|
+
"policy_id"=>"01a1",
|
41
|
+
"headers"=>[
|
42
|
+
{"name"=>"bad-header", "value"=>"value123"}
|
43
|
+
]
|
44
|
+
}
|
45
|
+
secure_headers_policy = SecureHeadersPolicy.fromJson(secure_headers_policy_json)
|
46
|
+
context "secure header example, invalid header" do
|
47
|
+
it "returns false" do
|
48
|
+
expect(secure_headers_policy.headers.length).to eq(0)
|
51
49
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
50
|
+
end
|
51
|
+
end
|
52
|
+
describe SecureHeadersPolicy do
|
53
|
+
secure_headers_policy_json = {
|
54
|
+
"policy_id"=>"01a1",
|
55
|
+
"headers"=>[
|
56
|
+
{"name"=>"x-permitted-cross-domain-policies", "value"=>"value123\\nabc"}
|
57
|
+
]
|
58
|
+
}
|
59
|
+
secure_headers_policy = SecureHeadersPolicy.fromJson(secure_headers_policy_json)
|
60
|
+
context "secure header, value is bad" do
|
61
|
+
it "returns false" do
|
62
|
+
expect(secure_headers_policy.headers.length).to eq(0)
|
65
63
|
end
|
64
|
+
end
|
66
65
|
end
|
67
|
-
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Logger do
|
4
|
+
|
5
|
+
describe "#add" do
|
6
|
+
context "with a warn logger" do
|
7
|
+
context "writing a debug message" do
|
8
|
+
it "should skip the tcell logic" do
|
9
|
+
expect(TCellAgent::Instrumentation).to_not receive(:safe_block_no_log)
|
10
|
+
|
11
|
+
logger = Logger.new('/dev/null')
|
12
|
+
|
13
|
+
logger.level = Logger::WARN
|
14
|
+
logger.add(Logger::DEBUG, "This will not be logged")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "writing a warn message" do
|
19
|
+
it "should run the tcell logic" do
|
20
|
+
expect(TCellAgent::Instrumentation).to receive(:safe_block_no_log).with("Handling JSAgent add")
|
21
|
+
|
22
|
+
logger = Logger.new('/dev/null')
|
23
|
+
|
24
|
+
logger.level = Logger::WARN
|
25
|
+
logger.add(Logger::WARN, "This will be logged")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "writing an error message" do
|
30
|
+
it "should run the tcell logic" do
|
31
|
+
expect(TCellAgent::Instrumentation).to receive(:safe_block_no_log).with("Handling JSAgent add")
|
32
|
+
|
33
|
+
logger = Logger.new('/dev/null')
|
34
|
+
|
35
|
+
logger.level = Logger::WARN
|
36
|
+
logger.add(Logger::ERROR, "This will be logged")
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with an empty message" do
|
40
|
+
it "should not run the context filter" do
|
41
|
+
expect(TCellAgent::Instrumentation).to receive(:safe_block_no_log).with(
|
42
|
+
"Handling JSAgent add"
|
43
|
+
).and_call_original
|
44
|
+
expect(TCellAgent).to receive(:policy).with(TCellAgent::PolicyTypes::DataLoss).and_return(
|
45
|
+
double("dlp_policy")
|
46
|
+
)
|
47
|
+
expect(TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS).to receive(
|
48
|
+
:fetch
|
49
|
+
).with(anything(), nil).and_return(double("request_env"))
|
50
|
+
|
51
|
+
logger = Logger.new('/dev/null')
|
52
|
+
|
53
|
+
logger.level = Logger::WARN
|
54
|
+
logger.add(Logger::ERROR, nil)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with no dlp policy" do
|
59
|
+
it "should not run the context filter" do
|
60
|
+
expect(TCellAgent::Instrumentation).to receive(:safe_block_no_log).with(
|
61
|
+
"Handling JSAgent add"
|
62
|
+
).and_call_original
|
63
|
+
expect(TCellAgent).to receive(:policy).with(TCellAgent::PolicyTypes::DataLoss).and_return(
|
64
|
+
nil
|
65
|
+
)
|
66
|
+
expect(TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS).to receive(
|
67
|
+
:fetch
|
68
|
+
).with(anything(), nil).and_return(double("request_env"))
|
69
|
+
|
70
|
+
logger = Logger.new('/dev/null')
|
71
|
+
|
72
|
+
logger.level = Logger::WARN
|
73
|
+
logger.add(Logger::ERROR, "My DLP Policy :(")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "with no request env" do
|
78
|
+
it "should not run the context filter" do
|
79
|
+
expect(TCellAgent::Instrumentation).to receive(:safe_block_no_log).with(
|
80
|
+
"Handling JSAgent add"
|
81
|
+
).and_call_original
|
82
|
+
expect(TCellAgent).to receive(:policy).with(TCellAgent::PolicyTypes::DataLoss).and_return(
|
83
|
+
double("dlp_policy")
|
84
|
+
)
|
85
|
+
expect(TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS).to receive(
|
86
|
+
:fetch
|
87
|
+
).with(anything(), nil).and_return(nil)
|
88
|
+
|
89
|
+
logger = Logger.new('/dev/null')
|
90
|
+
|
91
|
+
logger.level = Logger::WARN
|
92
|
+
logger.add(Logger::ERROR, "My DLP Policy :(")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "with a dlp policy, a message, and request env" do
|
97
|
+
|
98
|
+
context "with no tcell_context" do
|
99
|
+
it "should not run the context filter" do
|
100
|
+
request_env = double("request_env")
|
101
|
+
|
102
|
+
expect(TCellAgent::Instrumentation).to receive(:safe_block_no_log).with(
|
103
|
+
"Handling JSAgent add"
|
104
|
+
).and_call_original
|
105
|
+
expect(TCellAgent).to receive(:policy).with(TCellAgent::PolicyTypes::DataLoss).and_return(
|
106
|
+
double("dlp_policy")
|
107
|
+
)
|
108
|
+
expect(TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS).to receive(
|
109
|
+
:fetch
|
110
|
+
).with(anything(), nil).and_return(request_env)
|
111
|
+
expect(request_env).to receive(:[]).and_return(nil)
|
112
|
+
|
113
|
+
logger = Logger.new('/dev/null')
|
114
|
+
|
115
|
+
logger.level = Logger::WARN
|
116
|
+
logger.add(Logger::ERROR, "My DLP Policy :(")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "with tcell_context" do
|
121
|
+
it "should not run the context filter" do
|
122
|
+
request_env = double("request_env")
|
123
|
+
tcell_context = double("tcell_context")
|
124
|
+
|
125
|
+
expect(TCellAgent::Instrumentation).to receive(:safe_block_no_log).with(
|
126
|
+
"Handling JSAgent add"
|
127
|
+
).and_call_original
|
128
|
+
expect(TCellAgent).to receive(:policy).with(TCellAgent::PolicyTypes::DataLoss).and_return(
|
129
|
+
double("dlp_policy")
|
130
|
+
)
|
131
|
+
expect(TCellAgent::Instrumentation::Rails::Middleware::ContextMiddleware::THREADS).to receive(
|
132
|
+
:fetch
|
133
|
+
).with(anything(), nil).and_return(request_env)
|
134
|
+
expect(request_env).to receive(:[]).and_return(tcell_context)
|
135
|
+
expect(tcell_context).to receive(:filter_log).with("My DLP Policy :(")
|
136
|
+
|
137
|
+
logger = Logger.new('/dev/null')
|
138
|
+
|
139
|
+
logger.level = Logger::WARN
|
140
|
+
logger.add(Logger::ERROR, "My DLP Policy :(")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|