contrast-agent 4.1.0 → 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -0
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +22 -10
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +4 -3
- data/lib/contrast/agent.rb +5 -1
- data/lib/contrast/agent/assess.rb +0 -9
- data/lib/contrast/agent/assess/contrast_event.rb +49 -132
- data/lib/contrast/agent/assess/contrast_object.rb +54 -0
- data/lib/contrast/agent/assess/events/source_event.rb +4 -9
- data/lib/contrast/agent/assess/finalizers/hash.rb +7 -0
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +17 -3
- data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
- data/lib/contrast/agent/assess/policy/policy_node.rb +31 -59
- data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
- data/lib/contrast/agent/assess/policy/propagation_method.rb +41 -32
- data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
- data/lib/contrast/agent/assess/policy/propagator/append.rb +29 -15
- data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +21 -18
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
- data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -4
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +25 -17
- data/lib/contrast/agent/assess/policy/propagator/split.rb +83 -120
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +41 -25
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
- data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -8
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +13 -8
- data/lib/contrast/agent/assess/policy/trigger_node.rb +28 -7
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +59 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -3
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +6 -4
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +2 -4
- data/lib/contrast/agent/assess/properties.rb +0 -2
- data/lib/contrast/agent/assess/property/tagged.rb +56 -32
- data/lib/contrast/agent/assess/tracker.rb +16 -18
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
- data/lib/contrast/agent/middleware.rb +134 -55
- data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
- data/lib/contrast/agent/patching/policy/patch.rb +6 -0
- data/lib/contrast/agent/patching/policy/patch_status.rb +1 -1
- data/lib/contrast/agent/patching/policy/patcher.rb +51 -44
- data/lib/contrast/agent/patching/policy/trigger_node.rb +5 -2
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
- data/lib/contrast/agent/protect/rule/base.rb +63 -14
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +12 -28
- data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
- data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
- data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
- data/lib/contrast/agent/protect/rule/sqli.rb +20 -14
- data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
- data/lib/contrast/agent/reaction_processor.rb +1 -1
- data/lib/contrast/agent/request_context.rb +12 -0
- data/lib/contrast/agent/response.rb +5 -5
- data/lib/contrast/agent/rewriter.rb +3 -3
- data/lib/contrast/agent/scope.rb +81 -55
- data/lib/contrast/agent/static_analysis.rb +13 -7
- data/lib/contrast/agent/thread.rb +1 -1
- data/lib/contrast/agent/thread_watcher.rb +20 -5
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +18 -21
- data/lib/contrast/api/communication/response_processor.rb +8 -1
- data/lib/contrast/api/communication/socket_client.rb +22 -14
- data/lib/contrast/api/decorators.rb +2 -0
- data/lib/contrast/api/decorators/agent_startup.rb +58 -0
- data/lib/contrast/api/decorators/application_startup.rb +51 -0
- data/lib/contrast/api/decorators/library.rb +1 -0
- data/lib/contrast/api/decorators/library_usage_update.rb +1 -0
- data/lib/contrast/api/decorators/route_coverage.rb +15 -5
- data/lib/contrast/api/decorators/trace_event.rb +58 -42
- data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
- data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
- data/lib/contrast/api/decorators/user_input.rb +2 -1
- data/lib/contrast/common_agent_configuration.rb +2 -1
- data/lib/contrast/components/agent.rb +2 -0
- data/lib/contrast/components/app_context.rb +4 -22
- data/lib/contrast/components/assess.rb +36 -0
- data/lib/contrast/components/interface.rb +5 -3
- data/lib/contrast/components/sampling.rb +48 -6
- data/lib/contrast/components/scope.rb +72 -6
- data/lib/contrast/components/settings.rb +11 -7
- data/lib/contrast/config/assess_configuration.rb +2 -1
- data/lib/contrast/extension/assess/array.rb +2 -3
- data/lib/contrast/extension/assess/erb.rb +1 -3
- data/lib/contrast/extension/assess/exec_trigger.rb +1 -4
- data/lib/contrast/extension/assess/fiber.rb +2 -3
- data/lib/contrast/extension/assess/hash.rb +4 -2
- data/lib/contrast/extension/assess/kernel.rb +1 -2
- data/lib/contrast/extension/assess/marshal.rb +34 -26
- data/lib/contrast/extension/assess/regexp.rb +3 -8
- data/lib/contrast/extension/assess/string.rb +1 -2
- data/lib/contrast/framework/base_support.rb +51 -53
- data/lib/contrast/framework/manager.rb +16 -14
- data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
- data/lib/contrast/framework/rack/support.rb +2 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
- data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +1 -1
- data/lib/contrast/framework/rails/support.rb +44 -44
- data/lib/contrast/framework/sinatra/support.rb +102 -42
- data/lib/contrast/logger/application.rb +0 -3
- data/lib/contrast/logger/log.rb +31 -15
- data/lib/contrast/utils/class_util.rb +3 -1
- data/lib/contrast/utils/duck_utils.rb +1 -1
- data/lib/contrast/utils/heap_dump_util.rb +103 -87
- data/lib/contrast/utils/invalid_configuration_util.rb +21 -12
- data/lib/contrast/utils/object_share.rb +3 -3
- data/lib/contrast/utils/preflight_util.rb +1 -1
- data/lib/contrast/utils/resource_loader.rb +1 -1
- data/lib/contrast/utils/sha256_builder.rb +2 -2
- data/lib/contrast/utils/string_utils.rb +1 -1
- data/lib/contrast/utils/tag_util.rb +9 -13
- data/resources/assess/policy.json +12 -18
- data/resources/deadzone/policy.json +156 -0
- data/resources/protect/policy.json +12 -0
- data/ruby-agent.gemspec +61 -19
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +126 -113
- data/lib/contrast/agent/assess/rule.rb +0 -18
- data/lib/contrast/agent/assess/rule/base.rb +0 -52
- data/lib/contrast/agent/assess/rule/redos.rb +0 -67
- data/lib/contrast/framework/sinatra/patch/base.rb +0 -83
- data/lib/contrast/framework/sinatra/patch/support.rb +0 -27
- data/lib/contrast/utils/prevent_serialization.rb +0 -52
@@ -26,7 +26,7 @@ module Contrast
|
|
26
26
|
@_instrument ||= begin
|
27
27
|
::Rack::Session::Cookie.class_eval do
|
28
28
|
alias_method :cs__patched_initialize, :initialize
|
29
|
-
def initialize app, options = {}
|
29
|
+
def initialize app, options = {} # rubocop:disable Style/OptionHash
|
30
30
|
Contrast::Framework::Rack::Patch::SessionCookie.analyze(options)
|
31
31
|
cs__patched_initialize(app, options)
|
32
32
|
end
|
@@ -9,7 +9,8 @@ module Contrast
|
|
9
9
|
module Rack
|
10
10
|
# Used when Rack is present to define framework specific behavior. For
|
11
11
|
# now, the only part of this implemented is the Patch Support.
|
12
|
-
|
12
|
+
module Support
|
13
|
+
extend Contrast::Framework::BaseSupport
|
13
14
|
extend Contrast::Framework::Rack::Patch::Support
|
14
15
|
class << self
|
15
16
|
def detection_class
|
@@ -7,7 +7,7 @@ module Contrast
|
|
7
7
|
module Patch
|
8
8
|
# This class acts as our patch into the ActionController::Live::Buffer
|
9
9
|
# class, allowing us to track the close event on streamed responses.
|
10
|
-
|
10
|
+
module ActionControllerLiveBuffer
|
11
11
|
class << self
|
12
12
|
def send_messages
|
13
13
|
return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
|
@@ -10,7 +10,7 @@ module Contrast
|
|
10
10
|
# for the runtime detection of insecure configurations on individual
|
11
11
|
# ActionDispatch::Session::AbstractStore instances within the
|
12
12
|
# application.
|
13
|
-
|
13
|
+
module RailsApplicationConfiguration
|
14
14
|
def self.instrument
|
15
15
|
@_instrument ||= begin
|
16
16
|
::Rails::Application::Configuration.class_eval do
|
@@ -12,7 +12,7 @@ module Contrast
|
|
12
12
|
# TODO: RUBY-714 remove w/ EOL of 2.5
|
13
13
|
# @deprecated Changes to this class are discouraged as this approach is
|
14
14
|
# being phased out with support for those language versions.
|
15
|
-
|
15
|
+
module ActionControllerRailtiesHelperInherited
|
16
16
|
def self.instrument
|
17
17
|
@_instrument ||= begin
|
18
18
|
::ActionController::Railties::Helpers.class_eval do
|
@@ -14,7 +14,7 @@ module Contrast
|
|
14
14
|
# TODO: RUBY-714 remove w/ EOL of 2.5
|
15
15
|
# @deprecated Changes to this class are discouraged as this approach is
|
16
16
|
# being phased out with support for those language versions.
|
17
|
-
|
17
|
+
module ActiveRecordAttributeMethodsRead
|
18
18
|
def self.instrument
|
19
19
|
@_instrument ||= begin
|
20
20
|
::ActiveRecord::AttributeMethods::Read::ClassMethods.class_eval do
|
@@ -9,7 +9,7 @@ module Contrast
|
|
9
9
|
# TODO: RUBY-714 remove w/ EOL of 2.5
|
10
10
|
# @deprecated Changes to this class are discouraged as this approach is
|
11
11
|
# being phased out with support for those language versions.
|
12
|
-
|
12
|
+
module ActiveRecordTimeZoneInherited
|
13
13
|
def self.instrument
|
14
14
|
@_instrument ||= begin
|
15
15
|
::ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods.class_eval do
|
@@ -10,7 +10,8 @@ module Contrast
|
|
10
10
|
module Framework
|
11
11
|
module Rails
|
12
12
|
# Used when Rails is present to define framework specific behavior
|
13
|
-
class Support
|
13
|
+
class Support
|
14
|
+
extend Contrast::Framework::BaseSupport
|
14
15
|
extend Contrast::Framework::Rails::Patch::Support
|
15
16
|
|
16
17
|
class << self
|
@@ -46,32 +47,34 @@ module Contrast
|
|
46
47
|
end
|
47
48
|
|
48
49
|
# Find the current route, based on the provided Request wrapper
|
50
|
+
#
|
49
51
|
# @param request[Contrast::Agent::Request]
|
50
52
|
# @return [Contrast::Api::Dtm::RouteCoverage]
|
51
53
|
def current_route request
|
52
54
|
return unless ::Rails.cs__respond_to?(:application)
|
53
55
|
|
54
|
-
|
55
|
-
# precedence
|
56
|
-
# match_data: ActionDispatch::Journey::Path::Pattern::MatchData
|
57
|
-
# path_parameters: hash of various things
|
58
|
-
# route: ActionDispatch::Journey::Route
|
59
|
-
full_routes = ::Rails.application.routes.router.send(:find_routes, request.rack_request)
|
60
|
-
return if full_routes.empty?
|
56
|
+
match, _params, route, path = get_full_route(request.rack_request)
|
61
57
|
|
62
|
-
|
58
|
+
original_url = request.rack_request.path_info
|
63
59
|
|
64
|
-
# the route
|
65
|
-
if
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
60
|
+
# Route is either the final rails route, or a router that points to a Sinatra controller.
|
61
|
+
if Contrast::Framework::Sinatra::Support.sinatra_controller?(route.app.app)
|
62
|
+
# Create a request copied from current request, but with the base path removed from path_info.
|
63
|
+
new_req = ::ActionDispatch::Request.new(request.env)
|
64
|
+
new_req.path_info = new_req.path_info.gsub((path << match).join, '')
|
65
|
+
|
66
|
+
return Contrast::Framework::Sinatra::Support.current_route(new_req, route.app.app, original_url)
|
70
67
|
end
|
68
|
+
|
69
|
+
Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(route, original_url)
|
71
70
|
rescue StandardError => _e
|
72
71
|
nil
|
73
72
|
end
|
74
73
|
|
74
|
+
# Copy a request for modification.
|
75
|
+
#
|
76
|
+
# @param [::ActionDispatch::Request] original env.
|
77
|
+
# @return [::ActionDispatch::Request] a copy of original env with rails env merged.
|
75
78
|
def retrieve_request env
|
76
79
|
rails_env = ::Rails.application.env_config.merge(env)
|
77
80
|
::ActionDispatch::Request.new(rails_env || env)
|
@@ -86,37 +89,34 @@ module Contrast
|
|
86
89
|
|
87
90
|
private
|
88
91
|
|
89
|
-
# route is
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
# Determine if route is a Rails engine route.
|
93
|
+
#
|
94
|
+
# @param [Object] app or route that points to a ::Rails::Engine
|
95
|
+
# @return [bool] whether the router is an engine or not.
|
96
|
+
def engine_route? route
|
97
|
+
route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) && route.app.app < ::Rails::Engine
|
93
98
|
end
|
94
99
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
# filter for current http method
|
116
|
-
reportable_routes = engine_router.send(:match_routes, matching_routes, request.rack_request)
|
117
|
-
return if reportable_routes.empty?
|
118
|
-
|
119
|
-
Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(reportable_routes[0])
|
100
|
+
# Recursively get final route traversing engines as required.
|
101
|
+
#
|
102
|
+
# @param request [::Rack::Request] the rack request as will be handed to rails controller.
|
103
|
+
# @param top_router [::ActionDispatch::Journer::Router] the current router relative to the previous.
|
104
|
+
# @param path [Array<String>] the chunks of path that have been seen.
|
105
|
+
# @return [Array<array>] the final set of rails route classes.
|
106
|
+
def get_full_route request, top_router = ::Rails.application.routes.router, path = []
|
107
|
+
return if (route_matches = top_router.send(:find_routes, request)).empty?
|
108
|
+
|
109
|
+
match, params, route = route_matches.first
|
110
|
+
|
111
|
+
# If the current routing node points to a sub-app (::Rais::Engine), dive deeper.
|
112
|
+
# Have sub-app route the remainder of the url.
|
113
|
+
if engine_route?(route)
|
114
|
+
new_req = retrieve_request request.env
|
115
|
+
new_req.path_info = new_req.path_info.gsub(match.to_s, '')
|
116
|
+
get_full_route(new_req, route.app.app.routes.router, path << match.to_s)
|
117
|
+
else
|
118
|
+
[match, params, route, path]
|
119
|
+
end
|
120
120
|
end
|
121
121
|
|
122
122
|
# Rails engine routes need to be detected by inspecting Engine class route set
|
@@ -2,14 +2,13 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/framework/base_support'
|
5
|
-
require 'contrast/framework/sinatra/patch/support'
|
6
5
|
|
7
6
|
module Contrast
|
8
7
|
module Framework
|
9
8
|
module Sinatra
|
10
9
|
# Used when Sinatra is present to define framework specific behavior
|
11
|
-
class Support
|
12
|
-
extend Contrast::Framework::
|
10
|
+
class Support
|
11
|
+
extend Contrast::Framework::BaseSupport
|
13
12
|
class << self
|
14
13
|
def detection_class
|
15
14
|
'Sinatra'
|
@@ -20,44 +19,74 @@ module Contrast
|
|
20
19
|
end
|
21
20
|
|
22
21
|
def application_name
|
23
|
-
|
24
|
-
|
25
|
-
app_class.cs__class.cs__name
|
22
|
+
app_class&.cs__name
|
26
23
|
end
|
27
24
|
|
28
25
|
def application_root
|
29
|
-
|
30
|
-
|
31
|
-
app_class.root
|
26
|
+
app_instance&.root
|
32
27
|
end
|
33
28
|
|
34
29
|
def server_type
|
35
30
|
'sinatra'
|
36
31
|
end
|
37
32
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
33
|
+
# Given an object, determine if it is a Sinatra controller with routes.
|
34
|
+
#
|
35
|
+
# @param app [Object] suspected Sinatra app.
|
36
|
+
# @return [Boolean]
|
37
|
+
def sinatra_controller? app
|
38
|
+
# Sinatra is loaded?
|
39
|
+
return false unless defined?(::Sinatra) && defined?(::Sinatra::Base)
|
40
|
+
# App is a subclass of or actually is ::Sinatra::Base.
|
41
|
+
return false unless (app.cs__respond_to?(:<) && app < ::Sinatra::Base) || app == ::Sinatra::Base
|
42
|
+
|
43
|
+
# App has routes.
|
44
|
+
!app.routes.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Find all classes that subclass ::Sinatra::Base. Gather their routes.
|
48
|
+
#
|
49
|
+
# @return [Array<Contrast::Api::Dtm::RouteCoverage>] the routes found as Dtms.
|
41
50
|
def collect_routes
|
51
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless defined?(::Sinatra) && defined?(::Sinatra::Base)
|
52
|
+
|
42
53
|
routes = []
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
class_routes.each_pair do |method, list|
|
49
|
-
# item: [ Mustermann::Sinatra, [], Proc]
|
50
|
-
list.each do |item|
|
51
|
-
routes << Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(clazz, method, item[0])
|
54
|
+
sinatra_controllers.each do |controller|
|
55
|
+
controller.routes.each_pair do |method, route_triplets|
|
56
|
+
# Sinatra stores its routes as a triplet: [Mustermann::Sinatra, [], Proc]
|
57
|
+
route_triplets.map(&:first).each do |route_pattern|
|
58
|
+
routes << Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(controller, method, route_pattern)
|
52
59
|
end
|
53
60
|
end
|
54
61
|
end
|
55
62
|
routes
|
56
63
|
end
|
57
64
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
65
|
+
# Given the current request return a RouteCoverage dtm.
|
66
|
+
#
|
67
|
+
# @param request [Contrast::Agent::Request] a contrast tracked request.
|
68
|
+
# @param controller [::Sinatra::Base] optionally use this controller instead of global ::Sinatra::Base.
|
69
|
+
# @return [Contrast::Api::Dtm::RouteCoverage, nil] a Dtm describing the route
|
70
|
+
# matched to the request if a match was found.
|
71
|
+
def current_route request, controller = ::Sinatra::Base, full_route = nil
|
72
|
+
return unless sinatra_controller?(controller)
|
73
|
+
|
74
|
+
method = request.env[::Rack::REQUEST_METHOD] # GET, PUT, POST, etc...
|
75
|
+
|
76
|
+
# Find route match--checking superclasses if necessary.
|
77
|
+
final_controller, route_pattern = _route_recurse(controller, method, _cleaned_route(request))
|
78
|
+
return unless !final_controller.nil? && !route_pattern.nil?
|
79
|
+
|
80
|
+
full_route ||= request.path_info
|
81
|
+
|
82
|
+
Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(final_controller, method, route_pattern, full_route)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Search object space for sinatra controllers--any class that subclasses ::Sinatra::Base.
|
86
|
+
#
|
87
|
+
# @return [Array<::Sinatra::Base>] sinatra controlelrs
|
88
|
+
def sinatra_controllers
|
89
|
+
[::Sinatra::Base] + ObjectSpace.each_object(Class).select { |clazz| sinatra_controller?(clazz) }
|
61
90
|
end
|
62
91
|
|
63
92
|
def retrieve_request env
|
@@ -66,30 +95,61 @@ module Contrast
|
|
66
95
|
|
67
96
|
private
|
68
97
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
98
|
+
# Given a controller and a route to match against, find the route_pattern and class that will serve the
|
99
|
+
# route. This is recursive as Sinatra's routing is recursive from subclass to super.
|
100
|
+
#
|
101
|
+
# @param controller [Sinatra::Base, #routes] a Sinatra application.
|
102
|
+
# @param method [::Rack::REQUEST_METHOD] GET, POST, PUT, etc...
|
103
|
+
# @param method [String] the relative route passed from Rack.
|
104
|
+
# @return [Array[Sinatra::Base, Mustermann::Sinatra], nil] Either the controller that
|
105
|
+
# will handle the route along with the route pattern or nil if no match.
|
106
|
+
def _route_recurse controller, method, route
|
107
|
+
return if controller.nil? || controller.cs__class == NilClass
|
108
|
+
|
109
|
+
route_patterns = controller.routes.fetch(method, []).map(&:first)
|
110
|
+
route_pattern = route_patterns&.find do |matcher|
|
111
|
+
matcher.params(route) # ::Mustermann::Sinatra match.
|
76
112
|
end
|
113
|
+
|
114
|
+
return controller, route_pattern if route_pattern
|
115
|
+
|
116
|
+
# Check routes defined in superclass if present.
|
117
|
+
return _route_recurse(controller.superclass, method, route) if controller.superclass&.instance_variable_get(:@routes)
|
77
118
|
end
|
78
119
|
|
79
|
-
#
|
80
|
-
#
|
81
|
-
# Contrast::
|
82
|
-
|
83
|
-
|
120
|
+
# Get route and do some cleanup matching that of Sinatra::Base#process_route.
|
121
|
+
#
|
122
|
+
# @param request [Contrast::Agent::Request] a contrast tracked request.
|
123
|
+
# @return [String] the extracted and cleaned relative route.
|
124
|
+
def _cleaned_route request
|
125
|
+
settings = ::Sinatra::Base.settings
|
126
|
+
route = request.env[::Rack::PATH_INFO]
|
127
|
+
return '/' if route.empty? && !settings.empty_path_info?
|
128
|
+
|
129
|
+
!settings.strict_paths? && route.end_with?('/') ? route[0..-2] : route
|
130
|
+
end
|
131
|
+
|
132
|
+
# Almost an alias to app_instance.
|
133
|
+
#
|
134
|
+
# @return [::Sinatra::Base] the current controller class as routed by Rack.
|
135
|
+
def app_class
|
136
|
+
return unless defined?(::Sinatra) && defined?(::Sinatra::Base)
|
84
137
|
|
85
|
-
|
138
|
+
app_instance.cs__class
|
86
139
|
end
|
87
140
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
141
|
+
# Search the object space for the controller handling this request which will be
|
142
|
+
# the class inheriting from ::Sinatra::Base with @app=nil since it is the final servicer
|
143
|
+
# in the request/middleware chain.
|
144
|
+
#
|
145
|
+
# @return [::Sinatra::Base] the current controller as routed by Rack.
|
146
|
+
def app_instance
|
147
|
+
return unless defined?(::Sinatra) && defined?(::Sinatra::Base)
|
148
|
+
|
149
|
+
@_app_instance ||= begin
|
150
|
+
sinatra_layers = ObjectSpace.each_object(::Sinatra::Base).to_a
|
151
|
+
sinatra_layers.find { |layer| layer.app.nil? }
|
152
|
+
end
|
93
153
|
end
|
94
154
|
end
|
95
155
|
end
|
data/lib/contrast/logger/log.rb
CHANGED
@@ -37,28 +37,22 @@ module Contrast
|
|
37
37
|
# Given new settings from TeamServer, update our logging to use the new
|
38
38
|
# file and level, assuming they weren't set by local configuration.
|
39
39
|
#
|
40
|
-
# @param log_file [String] the file to which to log
|
41
|
-
# @param log_level [String] the level at which to log
|
40
|
+
# @param log_file [String] the file to which to log, as provided by TeamServer settings
|
41
|
+
# @param log_level [String] the level at which to log, as provided by TeamServer settings
|
42
42
|
def update log_file = nil, log_level = nil
|
43
|
-
|
44
|
-
|
45
|
-
config_path = config.path&.length.to_i.positive? ? config.path : nil
|
46
|
-
config_level = config.level&.length&.positive? ? config.level : nil
|
47
|
-
|
48
|
-
# config > settings > default
|
49
|
-
path = valid_path(config_path || log_file)
|
50
|
-
level_const = valid_level(config_level || log_level)
|
43
|
+
current_path = find_valid_path(log_file)
|
44
|
+
current_level_const = find_valid_level(log_level)
|
51
45
|
|
52
|
-
path_change =
|
53
|
-
level_change =
|
46
|
+
path_change = current_path != previous_path
|
47
|
+
level_change = current_level_const != previous_level
|
54
48
|
|
55
49
|
# don't needlessly recreate logger
|
56
50
|
return if @_logger && !(path_change || level_change)
|
57
51
|
|
58
|
-
@previous_path =
|
59
|
-
@previous_level =
|
52
|
+
@previous_path = current_path
|
53
|
+
@previous_level = current_level_const
|
60
54
|
|
61
|
-
@_logger = build(path:
|
55
|
+
@_logger = build(path: current_path, level_const: current_level_const)
|
62
56
|
# If we're logging to a new path, then let's start it w/ our helpful
|
63
57
|
# data gathering messages
|
64
58
|
log_update if path_change
|
@@ -108,6 +102,17 @@ module Contrast
|
|
108
102
|
logger.extend(Contrast::Logger::Time)
|
109
103
|
end
|
110
104
|
|
105
|
+
# Determine the valid path to which to log, given the precedence of config > settings > default.
|
106
|
+
#
|
107
|
+
# @param log_file [String, nil] the file to which to log as provided by the settings retrieved from the
|
108
|
+
# TeamServer.
|
109
|
+
# @return [String] the path to which to log or STDOUT / STDERR if one of those values provided.
|
110
|
+
def find_valid_path log_file
|
111
|
+
config = CONFIG.root.agent.logger
|
112
|
+
config_path = config.path&.length.to_i.positive? ? config.path : nil
|
113
|
+
valid_path(config_path || log_file)
|
114
|
+
end
|
115
|
+
|
111
116
|
def valid_path path
|
112
117
|
path = path.nil? ? Contrast::Utils::ObjectShare::EMPTY_STRING : path
|
113
118
|
return path if path == STDOUT_STR
|
@@ -129,6 +134,17 @@ module Contrast
|
|
129
134
|
end
|
130
135
|
end
|
131
136
|
|
137
|
+
# Determine the valid level to which to log, given the precedence of config > settings > default.
|
138
|
+
#
|
139
|
+
# @param log_level [String, nil] the level at which to log as provided by the settings retrieved from the
|
140
|
+
# TeamServer.
|
141
|
+
# @return [::Ougai::Logging::Severity] the level at which to log
|
142
|
+
def find_valid_level log_level
|
143
|
+
config = CONFIG.root.agent.logger
|
144
|
+
config_level = config.level&.length&.positive? ? config.level : nil
|
145
|
+
valid_level(config_level || log_level)
|
146
|
+
end
|
147
|
+
|
132
148
|
def valid_level level
|
133
149
|
level ||= DEFAULT_LEVEL
|
134
150
|
level = level.upcase
|