contrast-agent 3.14.0 → 3.15.0
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/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +18 -15
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +1 -0
- data/ext/cs__assess_string/cs__assess_string.c +24 -25
- data/ext/cs__assess_string/cs__assess_string.h +3 -1
- data/ext/cs__common/cs__common.c +4 -2
- data/ext/cs__common/cs__common.h +1 -1
- data/lib/contrast.rb +1 -1
- data/lib/contrast/agent/assess.rb +1 -0
- data/lib/contrast/agent/assess/contrast_event.rb +4 -12
- data/lib/contrast/agent/assess/finalizers/freeze.rb +3 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +45 -1
- data/lib/contrast/agent/assess/policy/patcher.rb +1 -1
- data/lib/contrast/agent/assess/policy/policy.rb +0 -2
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +0 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +7 -11
- data/lib/contrast/agent/assess/policy/propagation_method.rb +50 -33
- data/lib/contrast/agent/assess/policy/propagator/append.rb +8 -5
- data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/center.rb +9 -5
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +5 -3
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +6 -3
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +4 -1
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +6 -6
- data/lib/contrast/agent/assess/policy/propagator/next.rb +7 -5
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +8 -5
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +8 -4
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +5 -2
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +7 -5
- data/lib/contrast/agent/assess/policy/propagator/select.rb +15 -7
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +14 -8
- data/lib/contrast/agent/assess/policy/propagator/split.rb +14 -8
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +30 -21
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +11 -5
- data/lib/contrast/agent/assess/policy/source_method.rb +85 -58
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +16 -11
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +38 -15
- data/lib/contrast/agent/assess/policy/trigger_node.rb +14 -13
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -1
- data/lib/contrast/agent/assess/properties.rb +2 -0
- data/lib/contrast/agent/assess/property/updated.rb +136 -0
- data/lib/contrast/agent/assess/tracker.rb +66 -0
- data/lib/contrast/agent/class_reopener.rb +7 -5
- data/lib/contrast/agent/middleware.rb +0 -1
- data/lib/contrast/agent/patching/policy/patcher.rb +13 -22
- data/lib/contrast/agent/patching/policy/policy.rb +1 -4
- data/lib/contrast/agent/response.rb +17 -6
- data/lib/contrast/agent/rewriter.rb +1 -3
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +1 -4
- data/lib/contrast/api/decorators/application_update.rb +2 -4
- data/lib/contrast/api/decorators/trace_event.rb +5 -5
- data/lib/contrast/components/app_context.rb +11 -9
- data/lib/contrast/components/config.rb +3 -13
- data/lib/contrast/components/contrast_service.rb +2 -2
- data/lib/contrast/config/application_configuration.rb +5 -2
- data/lib/contrast/config/service_configuration.rb +8 -2
- data/lib/contrast/configuration.rb +88 -47
- data/lib/contrast/extension/assess.rb +0 -2
- data/lib/contrast/extension/assess/array.rb +8 -5
- data/lib/contrast/extension/assess/erb.rb +6 -3
- data/lib/contrast/extension/assess/fiber.rb +9 -9
- data/lib/contrast/extension/assess/hash.rb +2 -3
- data/lib/contrast/extension/assess/kernel.rb +12 -5
- data/lib/contrast/extension/assess/marshal.rb +3 -2
- data/lib/contrast/extension/assess/regexp.rb +5 -4
- data/lib/contrast/extension/assess/string.rb +8 -10
- data/lib/contrast/framework/rack/patch/session_cookie.rb +12 -18
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +4 -10
- data/lib/contrast/framework/rails/support.rb +2 -0
- data/lib/contrast/logger/application.rb +11 -3
- data/lib/contrast/utils/assess/tracking_util.rb +48 -3
- data/lib/contrast/utils/duck_utils.rb +0 -10
- data/lib/contrast/utils/env_configuration_item.rb +2 -1
- data/lib/contrast/utils/invalid_configuration_util.rb +21 -19
- data/lib/contrast/utils/string_utils.rb +10 -5
- data/resources/assess/policy.json +0 -10
- data/ruby-agent.gemspec +16 -15
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +42 -21
- data/lib/contrast/agent/assess/finalizers/finalize.rb +0 -21
- data/lib/contrast/extension/assess/assess_extension.rb +0 -145
- data/lib/contrast/utils/freeze_util.rb +0 -32
@@ -7,21 +7,24 @@ module ERBPropagator
|
|
7
7
|
def result_tagger patcher, preshift, ret, _block
|
8
8
|
return unless preshift.args.length >= 1
|
9
9
|
|
10
|
+
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
11
|
+
return unless properties
|
12
|
+
|
10
13
|
used_binding = preshift.args[0]
|
11
14
|
binding_variable_set = used_binding.local_variables
|
12
15
|
|
13
16
|
erb_pre_result = preshift.object.src
|
14
17
|
binding_variable_set.each do |bound_var_symbol|
|
15
18
|
bound_variable_value = used_binding.local_variable_get(bound_var_symbol)
|
16
|
-
next unless
|
19
|
+
next unless Contrast::Agent::Assess::Tracker.tracked?(bound_variable_value)
|
17
20
|
next unless erb_pre_result.include?(bound_var_symbol.to_s)
|
18
21
|
|
19
22
|
start_index = ret.index(bound_variable_value)
|
20
23
|
next if start_index.nil?
|
21
24
|
|
22
|
-
|
25
|
+
properties.copy_from(bound_variable_value, ret, start_index)
|
23
26
|
end
|
24
|
-
|
27
|
+
properties.build_event(
|
25
28
|
patcher,
|
26
29
|
ret,
|
27
30
|
preshift.object,
|
@@ -54,7 +54,6 @@ module Contrast
|
|
54
54
|
class << self
|
55
55
|
def track_rb_fiber_yield fiber, _method, results
|
56
56
|
return unless ASSESS.enabled?
|
57
|
-
return unless Contrast::Utils::DuckUtils.trackable?(fiber)
|
58
57
|
|
59
58
|
# results will be nil if StopIteration was raised,
|
60
59
|
# otherwise an Array of the yielded arguments
|
@@ -62,11 +61,11 @@ module Contrast
|
|
62
61
|
|
63
62
|
with_contrast_scope do
|
64
63
|
results.each do |result|
|
65
|
-
|
66
|
-
next
|
64
|
+
result_properties = Contrast::Agent::Assess::Tracker.properties(result)
|
65
|
+
next unless result_properties
|
67
66
|
|
68
|
-
|
69
|
-
|
67
|
+
result_properties.splat_from(fiber, result)
|
68
|
+
result_properties.build_event(
|
70
69
|
FIBER_YIELD_NODE,
|
71
70
|
result,
|
72
71
|
fiber,
|
@@ -80,13 +79,14 @@ module Contrast
|
|
80
79
|
|
81
80
|
def track_rb_fiber_new fiber, _enum, _enum_method, underlying, _underlying_method
|
82
81
|
return unless ASSESS.enabled?
|
83
|
-
return unless Contrast::Utils::DuckUtils.trackable?(fiber)
|
84
|
-
return unless Contrast::Utils::DuckUtils.trackable?(underlying)
|
85
82
|
return unless underlying.is_a?(String) && !underlying.empty?
|
86
83
|
|
87
84
|
with_contrast_scope do
|
88
|
-
|
89
|
-
|
85
|
+
properties = Contrast::Agent::Assess::Tracker.properties(fiber)
|
86
|
+
return unless properties
|
87
|
+
|
88
|
+
properties.splat_from(underlying, fiber)
|
89
|
+
properties.build_event(
|
90
90
|
FIBER_NEW_NODE,
|
91
91
|
fiber,
|
92
92
|
underlying,
|
@@ -15,10 +15,9 @@ module Contrast
|
|
15
15
|
class << self
|
16
16
|
def cs__duplicate_and_freeze object
|
17
17
|
return object unless object.is_a?(String) && !object.cs__frozen?
|
18
|
-
return object unless
|
18
|
+
return object unless Contrast::Agent::Assess::Tracker.tracked?(object)
|
19
19
|
|
20
|
-
ret = object
|
21
|
-
object.cs__transfer_properties(ret)
|
20
|
+
ret = Contrast::Agent::Assess::Tracker.duplicate(object)
|
22
21
|
ret.cs__freeze
|
23
22
|
rescue StandardError
|
24
23
|
# we'll rescue this error, but we can't log it here as that will
|
@@ -39,12 +39,15 @@ module Contrast
|
|
39
39
|
# oh, and there's also %<name>type and %{name}... b/c of course there is
|
40
40
|
# -HM
|
41
41
|
def sprintf_tagger patcher, preshift, ret, _block
|
42
|
+
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
43
|
+
return unless properties
|
44
|
+
|
42
45
|
format_string = preshift.args[0]
|
43
46
|
args = preshift.args[1]
|
44
47
|
|
45
48
|
track_sprintf(ret, format_string, args)
|
46
49
|
|
47
|
-
|
50
|
+
properties.build_event(
|
48
51
|
patcher,
|
49
52
|
ret,
|
50
53
|
preshift.object,
|
@@ -85,12 +88,16 @@ module Contrast
|
|
85
88
|
private
|
86
89
|
|
87
90
|
def handle_sprintf_value value, result
|
88
|
-
|
91
|
+
properties = Contrast::Agent::Assess::Tracker.properties(result)
|
92
|
+
return unless properties
|
93
|
+
|
94
|
+
value_properties = Contrast::Agent::Assess::Tracker.properties(value)
|
95
|
+
return unless value_properties
|
89
96
|
|
90
|
-
|
91
|
-
|
97
|
+
value_properties.events.each do |event|
|
98
|
+
properties.events << event
|
92
99
|
end
|
93
|
-
|
100
|
+
properties.splat_from(value, result)
|
94
101
|
end
|
95
102
|
|
96
103
|
def handle_sprintf_array args, result
|
@@ -22,7 +22,7 @@ module Contrast
|
|
22
22
|
|
23
23
|
# Since we know this is the source of the trigger, we can do some
|
24
24
|
# optimization here and return when it is not tracked
|
25
|
-
return unless Contrast::
|
25
|
+
return unless Contrast::Agent::Assess::Tracker.tracked?(source)
|
26
26
|
|
27
27
|
# source might not be all the args passed in, but it is the one we care
|
28
28
|
# about. we could pass in all the args in the last param here if it
|
@@ -34,7 +34,8 @@ module Contrast
|
|
34
34
|
self,
|
35
35
|
ret,
|
36
36
|
source)
|
37
|
-
|
37
|
+
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
38
|
+
properties.copy_from(source, ret)
|
38
39
|
rescue StandardError => e
|
39
40
|
logger.error('Unable to determine if a trigger occurred in Marshal.load', e)
|
40
41
|
end
|
@@ -47,8 +47,6 @@ module Contrast
|
|
47
47
|
return if scope_for_current_ec.instance_variable_get(:@contrast_scope) > 1
|
48
48
|
|
49
49
|
target = info_hash[:back_ref]
|
50
|
-
return unless Contrast::Utils::DuckUtils.trackable?(target)
|
51
|
-
|
52
50
|
with_contrast_scope do
|
53
51
|
result = info_hash[:result]
|
54
52
|
return unless result
|
@@ -56,8 +54,11 @@ module Contrast
|
|
56
54
|
string = info_hash[:string]
|
57
55
|
return unless string
|
58
56
|
|
59
|
-
|
60
|
-
|
57
|
+
properties = Contrast::Agent::Assess::Tracker.properties(target)
|
58
|
+
return unless properties
|
59
|
+
|
60
|
+
properties.splat_from(string, target)
|
61
|
+
properties.build_event(
|
61
62
|
REGEXP_EQUAL_SQUIGGLE_NODE,
|
62
63
|
target,
|
63
64
|
self,
|
@@ -3,13 +3,6 @@
|
|
3
3
|
|
4
4
|
require 'contrast/agent/assess/policy/propagation_node'
|
5
5
|
require 'contrast/components/interface'
|
6
|
-
require 'contrast/extension/assess/assess_extension'
|
7
|
-
|
8
|
-
# This patch installs our extension as early as possible. The alternative is to
|
9
|
-
# litter our code with Contrast::Utils::DuckUtils.trackable? checks.
|
10
|
-
class String
|
11
|
-
include Contrast::Extension::Assess::AssessExtension
|
12
|
-
end
|
13
6
|
|
14
7
|
module Contrast
|
15
8
|
module Extension
|
@@ -40,16 +33,21 @@ module Contrast
|
|
40
33
|
def track_interpolation inputs, result
|
41
34
|
return unless AGENT.interpolation_enabled?
|
42
35
|
return if in_contrast_scope?
|
43
|
-
return unless inputs.any?(
|
36
|
+
return unless inputs.any? { |input| Contrast::Agent::Assess::Tracker.tracked?(input) }
|
44
37
|
|
45
38
|
with_contrast_scope do
|
39
|
+
properties = Contrast::Agent::Assess::Tracker.properties(result)
|
40
|
+
return unless properties
|
41
|
+
|
46
42
|
offset = 0
|
47
43
|
inputs.each do |input|
|
48
|
-
|
44
|
+
properties.copy_from(input, result, offset)
|
49
45
|
offset += input.length
|
50
46
|
end
|
51
|
-
|
47
|
+
properties.build_event(INTERPOLATION_NODE, result, inputs, result, inputs)
|
52
48
|
end
|
49
|
+
rescue StandardError => e
|
50
|
+
logger.error('Unable to track interpolation', e)
|
53
51
|
end
|
54
52
|
|
55
53
|
def instrument_string
|
@@ -67,12 +67,10 @@ module Contrast
|
|
67
67
|
options,
|
68
68
|
safe_default: false)
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
caller_locations(10, 9)[0])
|
75
|
-
end
|
70
|
+
cs__report_finding(
|
71
|
+
CS__SECURE_RULE_NAME,
|
72
|
+
options,
|
73
|
+
caller_locations(10, 9)[0])
|
76
74
|
rescue StandardError => e
|
77
75
|
begin
|
78
76
|
logger.error('Unable to track call to secure session', e)
|
@@ -88,12 +86,10 @@ module Contrast
|
|
88
86
|
safe_default: false,
|
89
87
|
comparison_type: :greater_than)
|
90
88
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
caller_locations(10, 9)[0])
|
96
|
-
end
|
89
|
+
cs__report_finding(
|
90
|
+
CS__SESSION_TIMEOUT_NAME,
|
91
|
+
options,
|
92
|
+
caller_locations(10, 9)[0])
|
97
93
|
rescue StandardError => e
|
98
94
|
begin
|
99
95
|
logger.error('Unable to track call to set session timeout', e)
|
@@ -105,12 +101,10 @@ module Contrast
|
|
105
101
|
def apply_httponly options
|
106
102
|
return unless vulnerable_setting?(:httponly, true, options)
|
107
103
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
caller_locations(10, 9)[0])
|
113
|
-
end
|
104
|
+
cs__report_finding(
|
105
|
+
CS__HTTPONLY_NAME,
|
106
|
+
options,
|
107
|
+
caller_locations(10, 9)[0])
|
114
108
|
rescue StandardError => e
|
115
109
|
begin
|
116
110
|
logger.error('Unable to track call to httponly', e)
|
@@ -12,7 +12,7 @@ module Contrast
|
|
12
12
|
module AssessConfiguration
|
13
13
|
include Contrast::Components::Interface
|
14
14
|
|
15
|
-
access_component :agent, :analysis, :logging
|
15
|
+
access_component :agent, :analysis, :logging
|
16
16
|
|
17
17
|
CS__SESSION_TIMEOUT_NAME = 'session-timeout'
|
18
18
|
SAFE_SESSION_TIMEOUT = (30 * 60 * 1000)
|
@@ -52,9 +52,7 @@ module Contrast
|
|
52
52
|
return unless vulnerable_setting?(:expire_after, SAFE_SESSION_TIMEOUT, args, comparison_type: :greater_than, safe_default: false)
|
53
53
|
|
54
54
|
rails_session_settings = args[1]
|
55
|
-
|
56
|
-
cs__report_finding(CS__SESSION_TIMEOUT_NAME, rails_session_settings, caller_locations(6, 5)[0])
|
57
|
-
end
|
55
|
+
cs__report_finding(CS__SESSION_TIMEOUT_NAME, rails_session_settings, caller_locations(3, 2)[0])
|
58
56
|
rescue StandardError => e
|
59
57
|
begin
|
60
58
|
logger.error('Unable to track call to set session timeout', e)
|
@@ -68,9 +66,7 @@ module Contrast
|
|
68
66
|
return unless vulnerable_setting?(:secure, true, args)
|
69
67
|
|
70
68
|
rails_session_settings = args[1]
|
71
|
-
|
72
|
-
cs__report_finding(CS__SECURE_RULE_NAME, rails_session_settings, caller_locations(6, 5)[0])
|
73
|
-
end
|
69
|
+
cs__report_finding(CS__SECURE_RULE_NAME, rails_session_settings, caller_locations(3, 2)[0])
|
74
70
|
rescue StandardError => e
|
75
71
|
begin
|
76
72
|
logger.error('Unable to track call to disable secure cookies', e)
|
@@ -84,9 +80,7 @@ module Contrast
|
|
84
80
|
return unless vulnerable_setting?(:httponly, true, args)
|
85
81
|
|
86
82
|
rails_session_settings = args[1]
|
87
|
-
|
88
|
-
cs__report_finding(CS__HTTPONLY_RULE_NAME, rails_session_settings, caller_locations(6, 5)[0])
|
89
|
-
end
|
83
|
+
cs__report_finding(CS__HTTPONLY_RULE_NAME, rails_session_settings, caller_locations(3, 2)[0])
|
90
84
|
rescue StandardError => e
|
91
85
|
begin
|
92
86
|
logger.error('Unable to track call to disable httponly in session cookie', e)
|
@@ -118,6 +118,8 @@ module Contrast
|
|
118
118
|
|
119
119
|
# Rails engine routes need to be detected by inspecting Engine class route set
|
120
120
|
def find_all_routes app, route_list
|
121
|
+
return route_list unless app.cs__respond_to?(:routes) && app.routes.cs__respond_to?(:routes)
|
122
|
+
|
121
123
|
app.routes.routes.each do |route|
|
122
124
|
if route.cs__respond_to?(:app) && route.app.cs__class == ActionDispatch::Routing::RouteSet::Dispatcher
|
123
125
|
route_list << Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(route)
|
@@ -33,9 +33,17 @@ module Contrast
|
|
33
33
|
def application_configuration
|
34
34
|
return unless info?
|
35
35
|
|
36
|
-
loggable = CONFIG.raw.
|
37
|
-
|
38
|
-
|
36
|
+
loggable = CONFIG.raw.loggable
|
37
|
+
info('Current configuration', configuration: loggable)
|
38
|
+
env_keys = ENV.keys.select { |env_key| env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER) }
|
39
|
+
env_items = env_keys.map { |env_key| Contrast::Utils::EnvConfigurationItem.new(env_key, nil) }
|
40
|
+
env_translations = env_items.each_with_object({}) do |conversion, hash|
|
41
|
+
hash[conversion.key] = conversion.dot_path_array.join('.')
|
42
|
+
end
|
43
|
+
info('Set by environment', overrides: env_translations)
|
44
|
+
rescue StandardError => e
|
45
|
+
puts e
|
46
|
+
sleep(5)
|
39
47
|
end
|
40
48
|
|
41
49
|
def application_libraries
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# Copyright (c) 2020 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/agent/assess/tracker'
|
4
5
|
require 'contrast/components/interface'
|
6
|
+
require 'contrast/utils/duck_utils'
|
5
7
|
|
6
8
|
module Contrast
|
7
9
|
module Utils
|
@@ -23,6 +25,16 @@ module Contrast
|
|
23
25
|
_tracked?(obj, 0)
|
24
26
|
end
|
25
27
|
|
28
|
+
# Public interface to our tracking check, isolating the internals
|
29
|
+
# required for recursion.
|
30
|
+
#
|
31
|
+
# @param obj [Object] the thing to check if tracked
|
32
|
+
# @return [Boolean] if the obj, or something in it if a collection, is
|
33
|
+
# tracked.
|
34
|
+
def trackable? obj
|
35
|
+
_trackable?(obj, 0)
|
36
|
+
end
|
37
|
+
|
26
38
|
private
|
27
39
|
|
28
40
|
# Sometimes things are nested inside of each other, such as an Array
|
@@ -49,10 +61,8 @@ module Contrast
|
|
49
61
|
obj.any? do |ele|
|
50
62
|
_tracked?(ele, idx) unless obj == ele
|
51
63
|
end
|
52
|
-
elsif Contrast::Utils::DuckUtils.quacks_to?(obj, :cs__tracked?)
|
53
|
-
obj.cs__tracked?
|
54
64
|
else
|
55
|
-
|
65
|
+
Contrast::Agent::Assess::Tracker.tracked?(obj)
|
56
66
|
end
|
57
67
|
rescue StandardError => e
|
58
68
|
# This is used to ask if a ton of objects are tracked. They may not
|
@@ -61,6 +71,41 @@ module Contrast
|
|
61
71
|
logger.warn('Failed to determine tracking', e, module: obj.cs__class)
|
62
72
|
false
|
63
73
|
end
|
74
|
+
|
75
|
+
# Sometimes things are nested inside of each other, such as an Array
|
76
|
+
# holding a Hash, holding that Array. In those cases, rather than
|
77
|
+
# entering an infinite loop, we'll break out.
|
78
|
+
# Right now, that level of nesting has been arbitrarily set to 10.
|
79
|
+
#
|
80
|
+
# @param obj [Object] the thing to check if trackable
|
81
|
+
# @param idx [Integer] the number of levels nested we've gone
|
82
|
+
# @return [Boolean] if the obj, or something in it if a collection, is
|
83
|
+
# trackable.
|
84
|
+
def _trackable? obj, idx
|
85
|
+
return false if obj.nil?
|
86
|
+
return false if idx > 10
|
87
|
+
|
88
|
+
idx += 1
|
89
|
+
if Contrast::Utils::DuckUtils.iterable_hash?(obj)
|
90
|
+
obj.each_pair do |k, v|
|
91
|
+
return true if _trackable?(k, idx)
|
92
|
+
return true if _trackable?(v, idx)
|
93
|
+
end
|
94
|
+
false
|
95
|
+
elsif Contrast::Utils::DuckUtils.iterable_enumerable?(obj)
|
96
|
+
obj.any? do |ele|
|
97
|
+
_trackable?(ele, idx) unless obj == ele
|
98
|
+
end
|
99
|
+
else
|
100
|
+
Contrast::Agent::Assess::Tracker.trackable?(obj)
|
101
|
+
end
|
102
|
+
rescue StandardError => e
|
103
|
+
# This is used to ask if a ton of objects are tracked. They may not
|
104
|
+
# all be iterable. Bad things could happen in some cases, like when
|
105
|
+
# checking a closed statement for SQL injection trigger events
|
106
|
+
logger.warn('Failed to determine trackable', e, module: obj.cs__class)
|
107
|
+
false
|
108
|
+
end
|
64
109
|
end
|
65
110
|
end
|
66
111
|
end
|
@@ -62,16 +62,6 @@ module Contrast
|
|
62
62
|
# otherwise, don't risk it
|
63
63
|
false
|
64
64
|
end
|
65
|
-
|
66
|
-
# This method will return true if the object being checked has been patched
|
67
|
-
# to have the cs__properties field. We check the cs__tracked? method since it
|
68
|
-
# is side effect free (ie doesn't cause the properties object to be created).
|
69
|
-
def trackable? object
|
70
|
-
return true if object.cs__respond_to?(:cs__tracked?)
|
71
|
-
return false unless object.is_a?(Delegator)
|
72
|
-
|
73
|
-
object.cs__delegator_respond_to?(:cs__tracked?)
|
74
|
-
end
|
75
65
|
end
|
76
66
|
end
|
77
67
|
end
|
@@ -10,10 +10,11 @@ module Contrast
|
|
10
10
|
END_UNDERSCORE = /(_+)$/.cs__freeze
|
11
11
|
REPEATING_UNDERSCORE = /_{3,}/.cs__freeze
|
12
12
|
|
13
|
-
attr_reader :value, :dot_path_array
|
13
|
+
attr_reader :value, :dot_path_array, :key
|
14
14
|
|
15
15
|
def initialize key, value
|
16
16
|
key = EnvConfigurationItem.resolve_corrected_path(key)
|
17
|
+
@key = key
|
17
18
|
@dot_path_array = key.downcase.split(Contrast::Utils::ObjectShare::DOUBLE_UNDERSCORE)
|
18
19
|
@value = value
|
19
20
|
|
@@ -9,7 +9,7 @@ module Contrast
|
|
9
9
|
# customer applications, as determined by Configuration Rules at runtime.
|
10
10
|
module InvalidConfigurationUtil
|
11
11
|
include Contrast::Components::Interface
|
12
|
-
access_component :analysis, :app_context, :logging
|
12
|
+
access_component :analysis, :app_context, :logging, :scope
|
13
13
|
|
14
14
|
CS__PATH = 'path'
|
15
15
|
CS__SESSION_ID = 'sessionId'
|
@@ -23,28 +23,30 @@ module Contrast
|
|
23
23
|
# @param call_location [Thread::Backtrace::Location] the location where
|
24
24
|
# the bad configuration was set
|
25
25
|
def cs__report_finding rule_id, user_provided_options, call_location
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
with_contrast_scope do
|
27
|
+
finding = Contrast::Api::Dtm::Finding.new
|
28
|
+
finding.rule_id = rule_id
|
29
|
+
path = call_location.path
|
30
|
+
# just get the file name, not the full path
|
31
|
+
path = path.split(Contrast::Utils::ObjectShare::SLASH).last
|
32
|
+
session_id = user_provided_options[:key].to_s if user_provided_options
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
finding.version = Contrast::Agent::Assess::Policy::TriggerMethod::CURRENT_FINDING_VERSION
|
35
|
+
finding.properties[CS__SESSION_ID] = Contrast::Utils::StringUtils.force_utf8(session_id)
|
36
|
+
finding.properties[CS__PATH] = Contrast::Utils::StringUtils.force_utf8(path)
|
37
|
+
file_path = call_location.absolute_path
|
38
|
+
snippet = file_snippet(file_path, call_location)
|
39
|
+
finding.properties[CS__SNIPPET] = Contrast::Utils::StringUtils.force_utf8(snippet)
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
hash = Contrast::Utils::HashDigest.generate_config_hash(finding)
|
42
|
+
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash)
|
43
|
+
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
43
44
|
|
44
|
-
|
45
|
-
|
45
|
+
activity = Contrast::Api::Dtm::Activity.new
|
46
|
+
activity.findings << finding
|
46
47
|
|
47
|
-
|
48
|
+
Contrast::Agent.messaging_queue.send_event_eventually(activity)
|
49
|
+
end
|
48
50
|
rescue StandardError => e
|
49
51
|
logger.error('Unable to build a finding', e, rule: rule_id)
|
50
52
|
end
|