contrast-agent 4.7.0 → 4.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -1
- data/.rspec +0 -1
- data/.rspec_parallel +6 -0
- data/.simplecov +1 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.c +0 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.h +0 -2
- data/lib/contrast/agent/assess/contrast_event.rb +1 -5
- data/lib/contrast/agent/assess/finalizers/hash.rb +2 -5
- data/lib/contrast/agent/assess/policy/patcher.rb +5 -4
- data/lib/contrast/agent/assess/policy/policy.rb +1 -1
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +2 -6
- data/lib/contrast/agent/assess/policy/preshift.rb +11 -8
- data/lib/contrast/agent/assess/policy/propagation_method.rb +102 -59
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -7
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
- data/lib/contrast/agent/assess/policy/propagator/rack_protection.rb +73 -0
- data/lib/contrast/agent/assess/policy/propagator/split.rb +10 -6
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +3 -3
- data/lib/contrast/agent/assess/policy/propagator.rb +1 -0
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +6 -7
- data/lib/contrast/agent/assess/policy/source_method.rb +18 -22
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -4
- data/lib/contrast/agent/assess/policy/trigger_method.rb +61 -86
- data/lib/contrast/agent/assess/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/assess/property/evented.rb +2 -1
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +3 -4
- data/lib/contrast/agent/at_exit_hook.rb +3 -3
- data/lib/contrast/agent/class_reopener.rb +6 -5
- data/lib/contrast/agent/disable_reaction.rb +4 -5
- data/lib/contrast/agent/exclusion_matcher.rb +2 -7
- data/lib/contrast/agent/inventory/database_config.rb +117 -0
- data/lib/contrast/agent/inventory/dependency_analysis.rb +2 -6
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +8 -9
- data/lib/contrast/agent/inventory/policy/datastores.rb +5 -6
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/middleware.rb +15 -13
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +6 -3
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +21 -16
- data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/patch.rb +13 -8
- data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +14 -14
- data/lib/contrast/agent/patching/policy/policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/policy_node.rb +2 -3
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/policy.rb +1 -1
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +3 -5
- data/lib/contrast/agent/protect/rule/base.rb +10 -10
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +4 -5
- data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
- data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -5
- data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
- data/lib/contrast/agent/reaction_processor.rb +3 -4
- data/lib/contrast/agent/request.rb +9 -5
- data/lib/contrast/agent/request_context.rb +28 -31
- data/lib/contrast/agent/request_handler.rb +5 -3
- data/lib/contrast/agent/response.rb +2 -3
- data/lib/contrast/agent/rewriter.rb +4 -3
- data/lib/contrast/agent/rule_set.rb +5 -4
- data/lib/contrast/agent/service_heartbeat.rb +2 -3
- data/lib/contrast/agent/static_analysis.rb +7 -6
- data/lib/contrast/agent/thread.rb +2 -4
- data/lib/contrast/agent/thread_watcher.rb +3 -4
- data/lib/contrast/agent/tracepoint_hook.rb +10 -5
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +16 -11
- data/lib/contrast/api/communication/response_processor.rb +11 -11
- data/lib/contrast/api/communication/service_lifecycle.rb +9 -5
- data/lib/contrast/api/communication/socket_client.rb +18 -14
- data/lib/contrast/api/communication/speedracer.rb +5 -6
- data/lib/contrast/api/decorators/address.rb +2 -3
- data/lib/contrast/api/decorators/agent_startup.rb +7 -9
- data/lib/contrast/api/decorators/application_startup.rb +9 -10
- data/lib/contrast/api/decorators/application_update.rb +0 -4
- data/lib/contrast/api/decorators/http_request.rb +3 -7
- data/lib/contrast/api/decorators/instrumentation_mode.rb +3 -5
- data/lib/contrast/api/decorators/message.rb +7 -7
- data/lib/contrast/api/decorators/route_coverage.rb +24 -1
- data/lib/contrast/api/decorators/trace_event_object.rb +2 -3
- data/lib/contrast/components/agent.rb +13 -15
- data/lib/contrast/components/app_context.rb +7 -11
- data/lib/contrast/components/assess.rb +19 -16
- data/lib/contrast/components/base.rb +40 -0
- data/lib/contrast/components/config.rb +1 -2
- data/lib/contrast/components/contrast_service.rb +8 -11
- data/lib/contrast/components/heap_dump.rb +5 -4
- data/lib/contrast/components/inventory.rb +2 -7
- data/lib/contrast/components/logger.rb +14 -10
- data/lib/contrast/components/protect.rb +10 -13
- data/lib/contrast/components/sampling.rb +5 -5
- data/lib/contrast/components/scope.rb +9 -32
- data/lib/contrast/components/settings.rb +1 -5
- data/lib/contrast/config/base_configuration.rb +14 -6
- data/lib/contrast/configuration.rb +22 -19
- data/lib/contrast/extension/assess/array.rb +3 -15
- data/lib/contrast/extension/assess/eval_trigger.rb +2 -23
- data/lib/contrast/extension/assess/fiber.rb +6 -16
- data/lib/contrast/extension/assess/hash.rb +3 -13
- data/lib/contrast/extension/assess/kernel.rb +3 -14
- data/lib/contrast/extension/assess/marshal.rb +6 -14
- data/lib/contrast/extension/assess/regexp.rb +5 -15
- data/lib/contrast/extension/assess/string.rb +6 -31
- data/lib/contrast/extension/extension.rb +61 -0
- data/lib/contrast/extension/kernel.rb +2 -4
- data/lib/contrast/extension/protect/kernel.rb +0 -15
- data/lib/contrast/framework/grape/support.rb +174 -0
- data/lib/contrast/framework/manager.rb +44 -9
- data/lib/contrast/framework/rack/patch/session_cookie.rb +6 -6
- data/lib/contrast/framework/rack/support.rb +1 -1
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +5 -8
- data/lib/contrast/framework/rails/patch/support.rb +44 -37
- data/lib/contrast/framework/rails/railtie.rb +34 -0
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +4 -4
- data/lib/contrast/framework/rails/support.rb +60 -13
- data/lib/contrast/framework/sinatra/support.rb +1 -1
- data/lib/contrast/funchook/funchook.rb +4 -3
- data/lib/contrast/logger/application.rb +1 -6
- data/lib/contrast/logger/log.rb +103 -13
- data/lib/contrast/logger/request.rb +0 -4
- data/lib/contrast/tasks/config.rb +0 -1
- data/lib/contrast/tasks/service.rb +1 -6
- data/lib/contrast/utils/assess/sampling_util.rb +2 -3
- data/lib/contrast/utils/assess/tracking_util.rb +2 -4
- data/lib/contrast/utils/heap_dump_util.rb +5 -3
- data/lib/contrast/utils/invalid_configuration_util.rb +4 -3
- data/lib/contrast/utils/io_util.rb +3 -5
- data/lib/contrast/utils/job_servers_running.rb +4 -3
- data/lib/contrast/utils/os.rb +2 -3
- data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
- data/lib/contrast/utils/string_utils.rb +2 -3
- data/lib/contrast/utils/tag_util.rb +26 -19
- data/lib/contrast.rb +24 -14
- data/resources/assess/policy.json +252 -2
- data/resources/deadzone/policy.json +10 -0
- data/ruby-agent.gemspec +14 -3
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +104 -24
- data/lib/contrast/agent/railtie.rb +0 -31
- data/lib/contrast/components/interface.rb +0 -196
- data/lib/contrast/delegators/input_analysis.rb +0 -12
- data/lib/contrast/utils/inventory_util.rb +0 -114
@@ -1,7 +1,8 @@
|
|
1
1
|
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'contrast/components/
|
4
|
+
require 'contrast/components/logger'
|
5
|
+
require 'contrast/components/scope'
|
5
6
|
|
6
7
|
module Contrast
|
7
8
|
module Framework
|
@@ -11,9 +12,8 @@ module Contrast
|
|
11
12
|
# runtime detection of insecure configurations on individual cookies
|
12
13
|
# within the application
|
13
14
|
class SessionCookie
|
14
|
-
|
15
|
-
|
16
|
-
access_component :agent, :analysis, :logging, :scope
|
15
|
+
extend Contrast::Components::Logger::InstanceMethods
|
16
|
+
extend Contrast::Components::Scope::InstanceMethods
|
17
17
|
|
18
18
|
CS__SECURE_RULE_NAME = 'secure-flag-missing'
|
19
19
|
CS__HTTPONLY_NAME = 'rails-http-only-disabled'
|
@@ -36,8 +36,8 @@ module Contrast
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def analyze options
|
39
|
-
return unless AGENT.enabled?
|
40
|
-
return if ASSESS.forcibly_disabled?
|
39
|
+
return unless ::Contrast::AGENT.enabled?
|
40
|
+
return if ::Contrast::ASSESS.forcibly_disabled?
|
41
41
|
|
42
42
|
apply_session_timeout(options)
|
43
43
|
apply_httponly(options)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'contrast/components/interface'
|
5
4
|
require 'contrast/utils/invalid_configuration_util'
|
6
5
|
|
7
6
|
module Contrast
|
@@ -10,9 +9,7 @@ module Contrast
|
|
10
9
|
module Patch
|
11
10
|
# This module is used to analyze rails session storage configuration for assess vulnerabilities
|
12
11
|
module AssessConfiguration
|
13
|
-
include Contrast::Components::
|
14
|
-
|
15
|
-
access_component :agent, :analysis, :logging
|
12
|
+
include Contrast::Components::Logger::InstanceMethods
|
16
13
|
|
17
14
|
CS__SESSION_TIMEOUT_NAME = 'session-timeout'
|
18
15
|
SAFE_SESSION_TIMEOUT = (30 * 60 * 1000)
|
@@ -23,7 +20,7 @@ module Contrast
|
|
23
20
|
include Contrast::Utils::InvalidConfigurationUtil
|
24
21
|
|
25
22
|
def analyze_session_store *args
|
26
|
-
return if ASSESS.forcibly_disabled?
|
23
|
+
return if ::Contrast::ASSESS.forcibly_disabled?
|
27
24
|
|
28
25
|
apply_httponly_disabled(*args)
|
29
26
|
apply_secure_cookie_disabled(*args)
|
@@ -52,7 +49,7 @@ module Contrast
|
|
52
49
|
end
|
53
50
|
|
54
51
|
def apply_session_timeout *args
|
55
|
-
return if ASSESS.rule_disabled? CS__SESSION_TIMEOUT_NAME
|
52
|
+
return if ::Contrast::ASSESS.rule_disabled? CS__SESSION_TIMEOUT_NAME
|
56
53
|
return unless vulnerable_setting?(:expire_after, SAFE_SESSION_TIMEOUT, args,
|
57
54
|
comparison_type: :greater_than, safe_default: false)
|
58
55
|
|
@@ -67,7 +64,7 @@ module Contrast
|
|
67
64
|
end
|
68
65
|
|
69
66
|
def apply_secure_cookie_disabled *args
|
70
|
-
return if ASSESS.rule_disabled? CS__SECURE_RULE_NAME
|
67
|
+
return if ::Contrast::ASSESS.rule_disabled? CS__SECURE_RULE_NAME
|
71
68
|
return unless vulnerable_setting?(:secure, true, args)
|
72
69
|
|
73
70
|
rails_session_settings = args[1]
|
@@ -81,7 +78,7 @@ module Contrast
|
|
81
78
|
end
|
82
79
|
|
83
80
|
def apply_httponly_disabled *args
|
84
|
-
return if ASSESS.rule_disabled? CS__HTTPONLY_RULE_NAME
|
81
|
+
return if ::Contrast::ASSESS.rule_disabled? CS__HTTPONLY_RULE_NAME
|
85
82
|
return unless vulnerable_setting?(:httponly, true, args)
|
86
83
|
|
87
84
|
rails_session_settings = args[1]
|
@@ -20,47 +20,54 @@ module Contrast
|
|
20
20
|
# (i.e., where we normally patch) we will miss the configuration
|
21
21
|
# and will never be able to report session misconfiguration rules.
|
22
22
|
Contrast::Framework::Rails::Patch::RailsApplicationConfiguration.instrument
|
23
|
-
require 'contrast/
|
23
|
+
require 'contrast/framework/rails/railtie' if ::Rails::VERSION::MAJOR.to_i >= 3
|
24
24
|
end
|
25
25
|
|
26
26
|
# (See BaseSupport#after_load_patches)
|
27
27
|
def after_load_patches
|
28
|
-
Set.new([
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
28
|
+
patches = Set.new([
|
29
|
+
Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
|
30
|
+
'ActionController::Live::Buffer',
|
31
|
+
'contrast/framework/rails/patch/action_controller_live_buffer',
|
32
|
+
instrumenting_module:
|
33
|
+
'Contrast::Framework::Rails::Patch::ActionControllerLiveBuffer'),
|
34
|
+
Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
|
35
|
+
'Rails::Application::Configuration',
|
36
|
+
'contrast/framework/rails/patch/rails_application_configuration',
|
37
|
+
method_to_instrument: :session_store,
|
38
|
+
instrumenting_module:
|
39
|
+
'Contrast::Framework::Rails::Patch::RailsApplicationConfiguration')
|
40
|
+
])
|
41
|
+
if RUBY_VERSION < '2.6.0'
|
42
|
+
patches.merge([
|
43
|
+
# TODO: RUBY-714 remove w/ EOL of 2.5
|
44
|
+
#
|
45
|
+
# @deprecated Everything past here is used for Rewriting and can
|
46
|
+
# be removed once we no longer support 2.5.
|
47
|
+
Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
|
48
|
+
'ActionController::Railties::Helper::ClassMethods',
|
49
|
+
'contrast/framework/rails/rewrite/action_controller_railties_helper_inherited',
|
50
|
+
method_to_instrument: :inherited,
|
51
|
+
instrumenting_module:
|
52
|
+
'Contrast::Framework::Rails::Rewrite::ActionControllerRailtiesHelperInherited'),
|
53
|
+
Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
|
54
|
+
'ActiveRecord::AttributeMethods::Read::ClassMethods',
|
55
|
+
'contrast/framework/rails/rewrite/active_record_attribute_methods_read',
|
56
|
+
instrumenting_module:
|
57
|
+
'Contrast::Framework::Rails::Rewrite::ActiveRecordAttributeMethodsRead'),
|
58
|
+
Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
|
59
|
+
'ActiveRecord::Scoping::Named::ClassMethods',
|
60
|
+
'contrast/framework/rails/rewrite/active_record_named',
|
61
|
+
instrumenting_module: 'Contrast::Framework::Rails::Rewrite::ActiveRecordNamed'),
|
62
|
+
Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
|
63
|
+
'ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods',
|
64
|
+
'contrast/framework/rails/rewrite/active_record_time_zone_inherited',
|
65
|
+
method_to_instrument: :inherited,
|
66
|
+
instrumenting_module:
|
67
|
+
'Contrast::Framework::Rails::Rewrite::ActiveRecordTimeZoneInherited')
|
68
|
+
])
|
69
|
+
end
|
70
|
+
patches
|
64
71
|
end
|
65
72
|
end
|
66
73
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/utils/job_servers_running'
|
5
|
+
require 'contrast/components/logger'
|
6
|
+
|
7
|
+
module Contrast
|
8
|
+
module Framework
|
9
|
+
module Rails
|
10
|
+
# A Railtie to allow for the automatic hooking of the Agent into a Rails application.
|
11
|
+
class Railtie < ::Rails::Railtie
|
12
|
+
include Contrast::Components::Logger::InstanceMethods
|
13
|
+
|
14
|
+
initializer 'Contrast Ruby Agent Initializer' do |app|
|
15
|
+
log_rails = defined?(Rails) && defined?(Rails.logger)
|
16
|
+
|
17
|
+
Rails.logger.debug("In railtie ::#{ app.middleware.inspect }") if log_rails
|
18
|
+
|
19
|
+
if ::Contrast::APP_CONTEXT.instrument_middleware_stack?
|
20
|
+
::Contrast::AGENT.insert_middleware(app)
|
21
|
+
else
|
22
|
+
Rails.logger.debug('Detected a running job server, skipping Contrast middleware insertion.') if log_rails
|
23
|
+
logger.debug('Disabling Contrast for process', p_id: Process.pid)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
rake_tasks do
|
28
|
+
load 'contrast/tasks/service.rb'
|
29
|
+
load 'contrast/tasks/config.rb'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
return unless RUBY_VERSION < '2.6.0' # TODO: RUBY-714 remove guard w/ EOL of 2.5
|
5
5
|
|
6
|
-
require 'contrast/components/
|
6
|
+
require 'contrast/components/logger'
|
7
7
|
|
8
8
|
module Contrast
|
9
9
|
module Framework
|
@@ -17,12 +17,12 @@ module Contrast
|
|
17
17
|
# @deprecated Changes to this class are discouraged as this approach is
|
18
18
|
# being phased out with support for those language versions.
|
19
19
|
class ActiveRecordNamed
|
20
|
-
include Contrast::Components::
|
21
|
-
|
20
|
+
include Contrast::Components::Logger::InstanceMethods
|
21
|
+
extend Contrast::Components::Logger::InstanceMethods
|
22
22
|
|
23
23
|
class << self
|
24
24
|
def rewrite mod, method_name, body
|
25
|
-
return body unless AGENT.rewrite_interpolation?
|
25
|
+
return body unless ::Contrast::AGENT.rewrite_interpolation?
|
26
26
|
return body unless body.is_a?(Proc)
|
27
27
|
|
28
28
|
location = body.source_location
|
@@ -13,6 +13,8 @@ module Contrast
|
|
13
13
|
class Support
|
14
14
|
extend Contrast::Framework::BaseSupport
|
15
15
|
extend Contrast::Framework::Rails::Patch::Support
|
16
|
+
include Contrast::Components::Logger::InstanceMethods
|
17
|
+
extend Contrast::Components::Logger::InstanceMethods
|
16
18
|
|
17
19
|
class << self
|
18
20
|
RAILS_MODULE_NAME_VERSION = Gem::Version.new('6.0.0')
|
@@ -49,31 +51,36 @@ module Contrast
|
|
49
51
|
# Find the current route, based on the provided Request wrapper
|
50
52
|
#
|
51
53
|
# @param request[Contrast::Agent::Request]
|
52
|
-
# @return [Contrast::Api::Dtm::RouteCoverage]
|
54
|
+
# @return [Contrast::Api::Dtm::RouteCoverage, nil]
|
53
55
|
def current_route request
|
54
56
|
return unless ::Rails.cs__respond_to?(:application)
|
55
57
|
|
58
|
+
# ActionDispatch::Journey::Path::Pattern::MatchData, Hash, ActionDispatch::Journey::Route, Array<String>
|
56
59
|
match, _params, route, path = get_full_route(request.rack_request)
|
60
|
+
unless route
|
61
|
+
logger.warn('Unable to determine the current route of this request')
|
62
|
+
return
|
63
|
+
end
|
57
64
|
|
58
65
|
original_url = request.rack_request.path_info
|
59
|
-
|
66
|
+
mounted_app = route&.app&.app
|
60
67
|
# Route is either the final rails route, or a router that points to a Sinatra controller.
|
61
|
-
if Contrast::Framework::Sinatra::Support.sinatra_controller?(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
return Contrast::Framework::Sinatra::Support.current_route(new_req, route.app.app, original_url)
|
68
|
+
if mounted_app && Contrast::Framework::Sinatra::Support.sinatra_controller?(mounted_app)
|
69
|
+
return mounted_sinatra_route(request, match, path, route, original_url)
|
70
|
+
end
|
71
|
+
if mounted_app && Contrast::Framework::Grape::Support.grape_controller?(mounted_app)
|
72
|
+
return mounted_grape_route(request, match, path, route, original_url)
|
67
73
|
end
|
68
74
|
|
69
75
|
Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(route, original_url)
|
70
|
-
rescue StandardError =>
|
76
|
+
rescue StandardError => e
|
77
|
+
logger.warn('Unable to determine the current route of this request', e)
|
71
78
|
nil
|
72
79
|
end
|
73
80
|
|
74
81
|
# Copy a request for modification.
|
75
82
|
#
|
76
|
-
# @param [::ActionDispatch::Request] original env.
|
83
|
+
# @param env [::ActionDispatch::Request] original env.
|
77
84
|
# @return [::ActionDispatch::Request] a copy of original env with rails env merged.
|
78
85
|
def retrieve_request env
|
79
86
|
rails_env = ::Rails.application.env_config.merge(env)
|
@@ -91,18 +98,21 @@ module Contrast
|
|
91
98
|
|
92
99
|
# Determine if route is a Rails engine route.
|
93
100
|
#
|
94
|
-
# @param [Object] app or route that points to a ::Rails::Engine
|
101
|
+
# @param route [Object] app or route that points to a ::Rails::Engine
|
95
102
|
# @return [bool] whether the router is an engine or not.
|
96
103
|
def engine_route? route
|
104
|
+
return false unless route&.app&.app
|
105
|
+
|
97
106
|
route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) && route.app.app < ::Rails::Engine
|
98
107
|
end
|
99
108
|
|
100
109
|
# Recursively get final route traversing engines as required.
|
101
110
|
#
|
102
111
|
# @param request [::Rack::Request] the rack request as will be handed to rails controller.
|
103
|
-
# @param top_router [::ActionDispatch::
|
112
|
+
# @param top_router [::ActionDispatch::Journey::Router] the current router relative to the previous.
|
104
113
|
# @param path [Array<String>] the chunks of path that have been seen.
|
105
|
-
# @return [Array<
|
114
|
+
# @return [Array<Object>] the final set of rails route classes.
|
115
|
+
# ActionDispatch::Journey::Path::Pattern::MatchData, Hash, ActionDispatch::Journey::Route, Array<String>
|
106
116
|
def get_full_route request, top_router = ::Rails.application.routes.router, path = []
|
107
117
|
return if (route_matches = top_router.send(:find_routes, request)).empty?
|
108
118
|
|
@@ -132,6 +142,43 @@ module Contrast
|
|
132
142
|
end
|
133
143
|
route_list
|
134
144
|
end
|
145
|
+
|
146
|
+
# @param request[Contrast::Agent::Request]
|
147
|
+
# @param match [ActionDispatch::Journey::Path::Pattern::MatchData]
|
148
|
+
# @param path [Array<String>] the path of this request, built out from each nested
|
149
|
+
# ActionDispatch::Journey::Path::Pattern::MatchData
|
150
|
+
# @param route [::ActionDispatch::Journey::Route]
|
151
|
+
# @param original_url [String] the full url of this request, including the mount
|
152
|
+
# @return [Contrast::Api::Dtm::RouteCoverage, nil]
|
153
|
+
def mounted_sinatra_route request, match, path, route, original_url
|
154
|
+
new_req = unmounted_route(request, match, path)
|
155
|
+
Contrast::Framework::Sinatra::Support.current_route(new_req, route.app.app, original_url)
|
156
|
+
end
|
157
|
+
|
158
|
+
# @param request[Contrast::Agent::Request]
|
159
|
+
# @param match [ActionDispatch::Journey::Path::Pattern::MatchData]
|
160
|
+
# @param path [Array<String>] the path of this request, built out from each nested
|
161
|
+
# ActionDispatch::Journey::Path::Pattern::MatchData
|
162
|
+
# @param route [::ActionDispatch::Journey::Route]
|
163
|
+
# @param original_url [String] the full url of this request, including the mount
|
164
|
+
# @return [Contrast::Api::Dtm::RouteCoverage, nil]
|
165
|
+
def mounted_grape_route request, match, path, route, original_url
|
166
|
+
new_req = unmounted_route(request, match, path)
|
167
|
+
Contrast::Framework::Grape::Support.current_route(new_req, route.app.app, original_url)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Create a request copied from current request, but with the base path removed from path_info, as that's
|
171
|
+
# the mount.
|
172
|
+
#
|
173
|
+
# @param request[Contrast::Agent::Request]
|
174
|
+
# @param match []
|
175
|
+
# @param path [String] the path of this request
|
176
|
+
# @return [::ActionDispatch::Request]
|
177
|
+
def unmounted_route request, match, path
|
178
|
+
new_req = ::ActionDispatch::Request.new(request.env)
|
179
|
+
new_req.path_info = new_req.path_info.gsub((path << match).join, '')
|
180
|
+
new_req
|
181
|
+
end
|
135
182
|
end
|
136
183
|
end
|
137
184
|
end
|
@@ -106,7 +106,7 @@ module Contrast
|
|
106
106
|
def _route_recurse controller, method, route
|
107
107
|
return if controller.nil? || controller.cs__class == NilClass
|
108
108
|
|
109
|
-
route_patterns = controller.routes.fetch(method
|
109
|
+
route_patterns = controller.routes.fetch(method) { [] }.map(&:first)
|
110
110
|
route_pattern = route_patterns&.find do |matcher|
|
111
111
|
matcher.params(route) # ::Mustermann::Sinatra match.
|
112
112
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'contrast/components/
|
4
|
+
require 'contrast/components/logger'
|
5
|
+
|
5
6
|
# This module is used to find funchook library and determine availability
|
6
7
|
module Funchook
|
7
|
-
|
8
|
-
|
8
|
+
extend Contrast::Components::Logger::InstanceMethods
|
9
|
+
|
9
10
|
attr_accessor :path
|
10
11
|
|
11
12
|
# Possible platform library files
|
@@ -1,16 +1,11 @@
|
|
1
1
|
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'contrast/components/interface'
|
5
|
-
|
6
4
|
module Contrast
|
7
5
|
module Logger
|
8
6
|
# Our decorator for the Ougai logger allowing for the logging of the
|
9
7
|
# application environment, used to provide context during troubleshooting.
|
10
8
|
module Application
|
11
|
-
include Contrast::Components::Interface
|
12
|
-
access_component :config
|
13
|
-
|
14
9
|
ENV_KEYS = %w[HOME PWD RACK_ENV RAILS_ENV RUBY_VERSION GEM_HOME GEM_PATH].cs__freeze
|
15
10
|
# Utility method to log some current ruby and rails information from environment
|
16
11
|
def application_environment
|
@@ -31,7 +26,7 @@ module Contrast
|
|
31
26
|
def application_configuration
|
32
27
|
return unless info?
|
33
28
|
|
34
|
-
loggable = CONFIG.loggable
|
29
|
+
loggable = ::Contrast::CONFIG.loggable
|
35
30
|
info('Current configuration', configuration: loggable)
|
36
31
|
env_keys = ENV.keys.select do |env_key|
|
37
32
|
env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER)
|
data/lib/contrast/logger/log.rb
CHANGED
@@ -5,21 +5,106 @@ require 'logger'
|
|
5
5
|
require 'ougai'
|
6
6
|
require 'singleton'
|
7
7
|
|
8
|
-
require 'contrast/components/interface'
|
9
8
|
require 'contrast/extension/module'
|
10
9
|
require 'contrast/logger/application'
|
11
10
|
require 'contrast/logger/format'
|
12
11
|
require 'contrast/logger/request'
|
13
12
|
require 'contrast/logger/time'
|
13
|
+
require 'contrast/components/config'
|
14
14
|
|
15
15
|
module Contrast
|
16
|
+
# This module allows us to dynamically weave timing into our code, so that only when the time is actually needed do
|
17
|
+
# we pay the penalty for that timing block
|
18
|
+
module TraceTiming
|
19
|
+
def methods_to_time
|
20
|
+
@_methods_to_time ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
# Store info about methods for later patching.
|
24
|
+
METHOD_INFO = Struct.new(:clazz, :method_name, :custom_msg, :aliased)
|
25
|
+
|
26
|
+
# Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
|
27
|
+
# logger set to TRACE.
|
28
|
+
#
|
29
|
+
# @params: clazz [Class] the class of the method to time.
|
30
|
+
# @params: method [Symbol] the method to time.
|
31
|
+
# @params: method [String] optional custom logging message.
|
32
|
+
def add_method_to_trace_timing clazz, method, msg = nil
|
33
|
+
methods_to_time.append(METHOD_INFO.new(clazz, method, msg, false))
|
34
|
+
enable_trace_timing if logger.level == ::Ougai::Logging::TRACE
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
|
38
|
+
# logger set to TRACE.
|
39
|
+
#
|
40
|
+
# @params: method_spec [METHOD_INFO] specs about the method to be timed.
|
41
|
+
# @params: class_method [Boolean] whether this is or isn't a class/module method.
|
42
|
+
def trace_time_class_method meth_spec, class_method
|
43
|
+
untimed_func_symbol = "untimed_#{ meth_spec.method_name }".to_sym
|
44
|
+
send_to = class_method ? meth_spec.clazz.cs__singleton_class : meth_spec.clazz
|
45
|
+
meth_spec.clazz.class_eval do
|
46
|
+
include Contrast::Components::Logger::InstanceMethods
|
47
|
+
extend Contrast::Components::Logger::InstanceMethods
|
48
|
+
|
49
|
+
send_to.send(:alias_method, untimed_func_symbol, meth_spec.method_name)
|
50
|
+
meth_spec.aliased = true
|
51
|
+
|
52
|
+
log_message = "Elapsed time for #{ meth_spec.method_name }."
|
53
|
+
log_message = meth_spec.custom_message if meth_spec.custom_msg
|
54
|
+
|
55
|
+
send_to.send(:define_method, meth_spec.method_name) do |*args, **kwargs, &block| # rubocop:disable Performance/Kernel/DefineMethod
|
56
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
57
|
+
rv = if kwargs.empty?
|
58
|
+
send(untimed_func_symbol, *args, &block)
|
59
|
+
else
|
60
|
+
send(untimed_func_symbol, *args, **kwargs, &block)
|
61
|
+
end
|
62
|
+
delta = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
63
|
+
logger.trace(log_message, elapsed: delta * 1000)
|
64
|
+
rv
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Enable trace timing of methods specified in @_methods_to_time via aliasing.
|
70
|
+
def enable_trace_timing
|
71
|
+
methods_to_time.each do |meth_spec|
|
72
|
+
next if meth_spec.aliased
|
73
|
+
|
74
|
+
is_class_method = meth_spec.clazz.singleton_methods(false).include?(meth_spec.method_name)
|
75
|
+
trace_time_class_method meth_spec, is_class_method
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Contrast
|
82
|
+
# Used as a wrapper around our logging. The module option specifically adds in a new method for error that raises the
|
83
|
+
# logged exception, used in testing so that we can see if anything unexpected happens without it being swallowed
|
84
|
+
# while still providing safe options for customers.
|
16
85
|
module Logger
|
17
|
-
#
|
18
|
-
|
86
|
+
# For development set following env var to raise logged exceptions instead of just logging.
|
87
|
+
if ENV['CONTRAST__AGENT__RUBY_MORE_COWBELL']
|
88
|
+
Ougai::Logger.class_eval do
|
89
|
+
alias_method :cs__error, :error
|
90
|
+
alias_method :cs__warn, :warn
|
91
|
+
|
92
|
+
def error *args, **kwargs
|
93
|
+
if kwargs.empty?
|
94
|
+
cs__error(*args)
|
95
|
+
else
|
96
|
+
cs__error(*args, **kwargs)
|
97
|
+
end
|
98
|
+
args.each { |arg| raise arg if arg && arg.cs__class < Exception }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# This class functions to serve as a wrapper around our logging, as we need to be able to dynamically update
|
104
|
+
# level based on updates to TeamServer.
|
19
105
|
class Log
|
20
106
|
include Singleton
|
21
|
-
include Contrast::
|
22
|
-
access_component :config
|
107
|
+
include ::Contrast::TraceTiming
|
23
108
|
|
24
109
|
DEFAULT_NAME = 'contrast.log'
|
25
110
|
DEFAULT_LEVEL = ::Ougai::Logging::Severity::INFO
|
@@ -33,8 +118,8 @@ module Contrast
|
|
33
118
|
update
|
34
119
|
end
|
35
120
|
|
36
|
-
# Given new settings from TeamServer, update our logging to use the new
|
37
|
-
#
|
121
|
+
# Given new settings from TeamServer, update our logging to use the new file and level, assuming they weren't
|
122
|
+
# set by local configuration.
|
38
123
|
#
|
39
124
|
# @param log_file [String] the file to which to log, as provided by TeamServer settings
|
40
125
|
# @param log_level [String] the level at which to log, as provided by TeamServer settings
|
@@ -51,6 +136,8 @@ module Contrast
|
|
51
136
|
@previous_path = current_path
|
52
137
|
@previous_level = current_level_const
|
53
138
|
|
139
|
+
enable_trace_timing if current_level_const == ::Ougai::Logging::TRACE
|
140
|
+
|
54
141
|
@_logger = build(path: current_path, level_const: current_level_const)
|
55
142
|
# If we're logging to a new path, then let's start it w/ our helpful
|
56
143
|
# data gathering messages
|
@@ -60,6 +147,9 @@ module Contrast
|
|
60
147
|
logger.error('Unable to process update to LoggerManager.', e)
|
61
148
|
else
|
62
149
|
puts 'Unable to process update to LoggerManager.'
|
150
|
+
raise e if ENV['CONTRAST__AGENT__RUBY_MORE_COWBELL']
|
151
|
+
|
152
|
+
puts e.message
|
63
153
|
puts e.backtrace.join("\n")
|
64
154
|
end
|
65
155
|
end
|
@@ -107,8 +197,8 @@ module Contrast
|
|
107
197
|
# TeamServer.
|
108
198
|
# @return [String] the path to which to log or STDOUT / STDERR if one of those values provided.
|
109
199
|
def find_valid_path log_file
|
110
|
-
config = CONFIG.root.agent.logger
|
111
|
-
config_path = config
|
200
|
+
config = ::Contrast::CONFIG.root.agent.logger
|
201
|
+
config_path = config&.path&.length.to_i.positive? ? config.path : nil
|
112
202
|
valid_path(config_path || log_file)
|
113
203
|
end
|
114
204
|
|
@@ -141,8 +231,9 @@ module Contrast
|
|
141
231
|
# TeamServer.
|
142
232
|
# @return [::Ougai::Logging::Severity] the level at which to log
|
143
233
|
def find_valid_level log_level
|
144
|
-
config = CONFIG.root.agent.logger
|
145
|
-
config_level = config
|
234
|
+
config = ::Contrast::CONFIG.root.agent.logger
|
235
|
+
config_level = config&.level&.length&.positive? ? config.level : nil
|
236
|
+
|
146
237
|
valid_level(config_level || log_level)
|
147
238
|
end
|
148
239
|
|
@@ -158,8 +249,7 @@ module Contrast
|
|
158
249
|
DEFAULT_LEVEL
|
159
250
|
end
|
160
251
|
|
161
|
-
# Log that the Agent log has changed and include some default information
|
162
|
-
# at the start of the log.
|
252
|
+
# Log that the Agent log has changed and include some default information at the start of the log.
|
163
253
|
def log_update
|
164
254
|
logger.debug('Initialized new contrast agent logger')
|
165
255
|
logger.debug_with_time('middleware: log environment') do
|