contrast-agent 3.8.5 → 3.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/cs__assess_array/cs__assess_array.c +1 -1
- data/ext/cs__assess_module/cs__assess_module.c +0 -1
- data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
- data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
- data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
- data/ext/cs__common/cs__common.c +6 -6
- data/ext/cs__common/cs__common.h +3 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
- data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
- data/funchook/autom4te.cache/requests +48 -48
- data/funchook/config.log +2 -2
- data/lib/contrast/agent.rb +15 -5
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/assess/contrast_event.rb +9 -8
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
- data/lib/contrast/agent/assess/policy/policy.rb +0 -14
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
- data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
- data/lib/contrast/agent/assess/properties.rb +29 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
- data/lib/contrast/agent/class_reopener.rb +98 -55
- data/lib/contrast/agent/feature_state.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/logger_manager.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
- data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
- data/lib/contrast/agent/patching/policy/patch.rb +54 -23
- data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
- data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
- data/lib/contrast/agent/patching/policy/policy.rb +4 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
- data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
- data/lib/contrast/agent/protect/policy/policy.rb +6 -6
- data/lib/contrast/agent/protect/rule/base.rb +1 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/railtie.rb +11 -5
- data/lib/contrast/agent/request.rb +1 -19
- data/lib/contrast/agent/request_context.rb +1 -1
- data/lib/contrast/agent/rewriter.rb +4 -3
- data/lib/contrast/agent/scope.rb +116 -19
- data/lib/contrast/agent/service_heartbeat.rb +5 -2
- data/lib/contrast/agent/settings_state.rb +12 -8
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api.rb +1 -0
- data/lib/contrast/api/speedracer.rb +2 -2
- data/lib/contrast/components/agent.rb +26 -7
- data/lib/contrast/components/app_context.rb +8 -45
- data/lib/contrast/components/contrast_service.rb +3 -4
- data/lib/contrast/components/interface.rb +1 -1
- data/lib/contrast/components/scope.rb +56 -26
- data/lib/contrast/config/ruby_configuration.rb +8 -3
- data/lib/contrast/delegators.rb +9 -0
- data/lib/contrast/delegators/application_update.rb +32 -0
- data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
- data/lib/contrast/extensions/framework/rack/request.rb +24 -0
- data/lib/contrast/extensions/framework/rack/response.rb +23 -0
- data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
- data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
- data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
- data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
- data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
- data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
- data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
- data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
- data/lib/contrast/extensions/ruby_core/module.rb +17 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
- data/lib/contrast/framework/base_support.rb +63 -0
- data/lib/contrast/framework/manager.rb +109 -0
- data/lib/contrast/framework/platform_version.rb +21 -0
- data/lib/contrast/framework/rails_support.rb +88 -0
- data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
- data/lib/contrast/framework/sinatra_support.rb +94 -0
- data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
- data/lib/contrast/utils/assess/tracking_util.rb +2 -4
- data/lib/contrast/utils/class_util.rb +92 -37
- data/lib/contrast/utils/duck_utils.rb +59 -39
- data/lib/contrast/utils/environment_util.rb +5 -75
- data/lib/contrast/utils/freeze_util.rb +3 -7
- data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
- data/lib/contrast/utils/job_servers_running.rb +39 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
- data/lib/contrast/utils/service_response_util.rb +0 -6
- data/lib/contrast/utils/sinatra_helper.rb +6 -0
- data/lib/contrast/utils/stack_trace_utils.rb +1 -1
- data/resources/assess/policy.json +74 -23
- data/resources/inventory/policy.json +1 -1
- data/resources/protect/policy.json +11 -9
- data/resources/rubocops/object/frozen_cop.rb +1 -1
- data/ruby-agent.gemspec +2 -0
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +94 -57
- data/ext/cs__scope/cs__scope.c +0 -96
- data/ext/cs__scope/cs__scope.h +0 -33
- data/lib/contrast/agent/assess/class_reverter.rb +0 -82
- data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
- data/lib/contrast/core_extensions/module.rb +0 -42
- data/lib/contrast/core_extensions/object.rb +0 -27
- data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
- data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
- data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
- data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
- data/lib/contrast/rails_extensions/buffer.rb +0 -30
- data/lib/contrast/rails_extensions/rack.rb +0 -45
- data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
- data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
- data/lib/contrast/utils/operating_environment.rb +0 -38
- data/lib/contrast/utils/path_util.rb +0 -151
- data/lib/contrast/utils/scope_util.rb +0 -99
@@ -0,0 +1,20 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ActionController
|
5
|
+
module Railties
|
6
|
+
# Used to monkey patch all the inherited calls in action_pack
|
7
|
+
#
|
8
|
+
# This is the usual entry point for Rails inheritance work, so it should
|
9
|
+
# catch most of the calls to inherited.
|
10
|
+
module Helpers
|
11
|
+
alias_method :cs__patched_inherited, :inherited
|
12
|
+
def inherited klass
|
13
|
+
klass&.instance_variable_set(:@cs__defining_class, true)
|
14
|
+
cs__patched_inherited(klass)
|
15
|
+
ensure
|
16
|
+
klass&.instance_variable_set(:@cs__defining_class, false)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module AttributeMethods
|
6
|
+
module Read
|
7
|
+
# Rails / ActiveRecord are sneaky a.f. They define attributes of a
|
8
|
+
# class in one method, then monkey patch allocate in another and
|
9
|
+
# finally invoke module_eval in this method... but of course they use a
|
10
|
+
# '_tmp_' header for the method name and then alias it in this module
|
11
|
+
# to name it what we expect
|
12
|
+
module ClassMethods
|
13
|
+
alias_method :cs__patched_define_method_attribute, :define_method_attribute
|
14
|
+
|
15
|
+
def define_method_attribute *args, &block
|
16
|
+
ret = cs__patched_define_method_attribute(*args, &block)
|
17
|
+
method_name = args[0]
|
18
|
+
Contrast::Agent::Assess::Policy::Patcher.patch_assess_method(self, method_name)
|
19
|
+
ret
|
20
|
+
end
|
21
|
+
|
22
|
+
protected :cs__patched_define_method_attribute, :define_method_attribute
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
cs__scoped_require 'contrast/components/interface'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module Scoping
|
8
|
+
module Named
|
9
|
+
# Our patch into the ActiveRecord::Scoping::Named::ClassMethods Module,
|
10
|
+
# allowing for the runtime rewrite of interpolation calls defined in
|
11
|
+
# methods defined dynamically during application execution.
|
12
|
+
#
|
13
|
+
# TODO: RUBY-534
|
14
|
+
module ClassMethods
|
15
|
+
include Contrast::Components::Interface
|
16
|
+
access_component :logging, :agent
|
17
|
+
|
18
|
+
def _cs__rewrite method_name, body
|
19
|
+
return body unless AGENT.rewrite_interpolation?
|
20
|
+
return body unless body.is_a?(Proc)
|
21
|
+
|
22
|
+
location = body.source_location
|
23
|
+
return body if location.nil?
|
24
|
+
|
25
|
+
# Good news, once we patch the body once, the source location
|
26
|
+
# becomes eval. We may need to fix this later though (so it may
|
27
|
+
# be bad news)
|
28
|
+
return body if location.empty? || location[0].empty? || location[0].include?('eval')
|
29
|
+
|
30
|
+
opener = Contrast::Agent::ClassReopener.new(Contrast::Agent::ModuleData.new(self))
|
31
|
+
original_source_code = opener.source_code(location, method_name)
|
32
|
+
return body unless original_source_code
|
33
|
+
return body if Contrast::Agent::Rewriter.send(:unrepeatable?, original_source_code)
|
34
|
+
return body unless Contrast::Agent::Rewriter.send(:interpolations?, original_source_code)
|
35
|
+
|
36
|
+
# the code looks like 'source :some_method_name, ->lambda_literal'
|
37
|
+
# we just need the lambda
|
38
|
+
body_start = original_source_code.index(',') + 1
|
39
|
+
original_source_code = original_source_code[body_start..-1]
|
40
|
+
|
41
|
+
new_method_source = Contrast::Agent::Rewriter.send(:rewrite_method, original_source_code)
|
42
|
+
return body unless Contrast::Agent::Rewriter.send(:valid_code?, new_method_source)
|
43
|
+
|
44
|
+
unbound_eval(cs__name, new_method_source)
|
45
|
+
rescue SyntaxError, StandardError => e
|
46
|
+
logger.debug(e, "Can't parse method source in scoped method #{ method_name }: #{ e.message }")
|
47
|
+
body
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
cs__scoped_require 'cs__assess_active_record_named/cs__assess_active_record_named'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module AttributeMethods
|
6
|
+
# Used to monkey patch all the inherited calls in action_pack
|
7
|
+
module TimeZoneConversion
|
8
|
+
module ClassMethods #:nodoc:
|
9
|
+
private
|
10
|
+
|
11
|
+
alias_method :cs__patched_inherited, :inherited
|
12
|
+
def inherited klass
|
13
|
+
klass&.instance_variable_set(:@cs__defining_class, true)
|
14
|
+
cs__patched_inherited(klass)
|
15
|
+
ensure
|
16
|
+
klass&.instance_variable_set(:@cs__defining_class, false)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
cs__scoped_require 'contrast/components/interface'
|
5
|
+
|
6
|
+
module ActionController
|
7
|
+
module Live
|
8
|
+
# This class acts as our patch into the ActionController::Live::Buffer
|
9
|
+
# class, allowing us to track the close event on streamed responses.
|
10
|
+
class Buffer
|
11
|
+
include Contrast::Components::Interface
|
12
|
+
access_component :contrast_service
|
13
|
+
|
14
|
+
# normally pre->in->post filters are applied however, in a streamed response
|
15
|
+
# we can run into a case where it's pre -> in -> post -> more infilters
|
16
|
+
# in order to submit anything found during the infilters after the response has been written we need to explicity send them
|
17
|
+
alias_method :cs__close, :close
|
18
|
+
def close
|
19
|
+
if (context = Contrast::Agent::REQUEST_TRACKER.current)
|
20
|
+
[context.server_activity, context.activity, context.observed_route].each do |msg|
|
21
|
+
CONTRAST_SERVICE.send_message msg
|
22
|
+
end
|
23
|
+
end
|
24
|
+
cs__close
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Rails
|
5
|
+
class Application
|
6
|
+
# Our patch into the Rails::Application::Configuration Class, allowing
|
7
|
+
# for the runtime detection of insecure configurations on individual
|
8
|
+
# ActionDispatch::Session::AbstractStore instances within the
|
9
|
+
# application.
|
10
|
+
class Configuration
|
11
|
+
include Contrast::Utils::InvalidConfigurationUtil
|
12
|
+
include Contrast::Components::Interface
|
13
|
+
|
14
|
+
# Note to self / PR reviewers / wizard people dear reader,
|
15
|
+
# including the components into Rails here may be unnecessary
|
16
|
+
# if we're not calling anything besides #analyze_session_store?
|
17
|
+
access_component :analysis, :scope
|
18
|
+
|
19
|
+
alias_method :cs__patched_session_store, :session_store
|
20
|
+
def session_store *args
|
21
|
+
ret = cs__patched_session_store(*args)
|
22
|
+
Contrast::Utils::RailsAssessConfiguration.analyze_session_store(*args)
|
23
|
+
ret
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
cs__scoped_require 'contrast/framework/sinatra_support'
|
5
|
+
|
6
|
+
module Sinatra
|
7
|
+
# Our patch into the Sinatra::Base Class, allowing for the inventory of the
|
8
|
+
# routes called by the application
|
9
|
+
class Base
|
10
|
+
alias_method :cs__patched_call!, :call!
|
11
|
+
|
12
|
+
# publicly available method for Sinatra::Base things -- unfortunately,
|
13
|
+
# getting the routes appear to require a lookup every time
|
14
|
+
def call! *args
|
15
|
+
cs__patched_map_route(*args)
|
16
|
+
cs__patched_call!(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Use logic copied from Sinatra::Base#process_route to determine which
|
22
|
+
# pattern matches the request being invoked by Sinatra::Base#call!
|
23
|
+
#
|
24
|
+
# @param args [Array<Object>] we rely on the @settings object in
|
25
|
+
# Sinatra::Base and the env variable passed in as args[0]
|
26
|
+
def cs__patched_map_route *args
|
27
|
+
context = Contrast::Agent::REQUEST_TRACKER.current
|
28
|
+
return unless context
|
29
|
+
|
30
|
+
env = args[0]
|
31
|
+
return unless env
|
32
|
+
|
33
|
+
# There isn't a Request object in the Sinatra::Base yet - it's made
|
34
|
+
# during the #call! method, which we're patching - so we need to
|
35
|
+
# access the env
|
36
|
+
method = env[Rack::REQUEST_METHOD]
|
37
|
+
route = env[Rack::PATH_INFO]
|
38
|
+
route = route.to_s
|
39
|
+
|
40
|
+
# get all potential routes for this request type
|
41
|
+
routes = settings.routes[method]
|
42
|
+
return unless routes
|
43
|
+
|
44
|
+
# Do some cleanup that matches Sinatra::Base#process_route
|
45
|
+
route = '/' if route.empty? && !settings.empty_path_info?
|
46
|
+
route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
|
47
|
+
|
48
|
+
# Find the route that matches this request. Since we're using
|
49
|
+
# settings, we should resolve in the same precedence as Sinatra
|
50
|
+
routes.each do |pattern, _, _| # Mustermann::Sinatra
|
51
|
+
next unless pattern.params(route)
|
52
|
+
|
53
|
+
dtm = Contrast::Framework::SinatraSupport.send(:route_to_coverage, cs__class, method, pattern)
|
54
|
+
context.append_route_coverage(dtm)
|
55
|
+
break
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -32,20 +32,21 @@ module Contrast
|
|
32
32
|
|
33
33
|
# classes that don't play nice w/ our standard propagation
|
34
34
|
cs__scoped_require 'contrast/agent/assess/frozen_properties'
|
35
|
-
cs__scoped_require 'contrast/
|
35
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/assess_extension'
|
36
36
|
# this needs to come first b/c array and others work on strings and
|
37
37
|
# expect them to be trackable
|
38
|
-
cs__scoped_require 'contrast/
|
38
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/string'
|
39
39
|
|
40
|
-
cs__scoped_require 'contrast/
|
41
|
-
cs__scoped_require 'contrast/
|
42
|
-
cs__scoped_require 'contrast/
|
43
|
-
cs__scoped_require 'contrast/
|
44
|
-
cs__scoped_require 'contrast/
|
45
|
-
cs__scoped_require 'contrast/
|
46
|
-
cs__scoped_require 'contrast/
|
47
|
-
cs__scoped_require 'contrast/
|
48
|
-
cs__scoped_require 'contrast/
|
40
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/array'
|
41
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/erb'
|
42
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/fiber'
|
43
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/hash'
|
44
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/kernel'
|
45
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/regexp'
|
46
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/module'
|
47
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/basic_object'
|
48
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/tilt_template_trigger'
|
49
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/xpath_library_trigger'
|
49
50
|
end
|
50
51
|
end
|
51
52
|
end
|
@@ -1,6 +1,7 @@
|
|
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
|
+
cs__scoped_require 'contrast/agent/patching/policy/patch'
|
4
5
|
cs__scoped_require 'contrast/agent/patching/policy/patcher'
|
5
6
|
cs__scoped_require 'contrast/components/interface'
|
6
7
|
|
@@ -21,7 +22,7 @@ class Array
|
|
21
22
|
'source' => 'O',
|
22
23
|
'target' => 'R',
|
23
24
|
'patch_class' => 'NOOP',
|
24
|
-
'patch_method' => '
|
25
|
+
'patch_method' => 'cs__track_join'
|
25
26
|
}.cs__freeze
|
26
27
|
ARRAY_JOIN_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(ARRAY_JOIN_HASH)
|
27
28
|
|
@@ -29,8 +30,8 @@ class Array
|
|
29
30
|
# Multiple Strings are appended with the #join method. Because that
|
30
31
|
# operation happens in C, we have to do it here rather than rely on the
|
31
32
|
# patch of our String append or concat methods.
|
32
|
-
def
|
33
|
-
return ret if Contrast::Agent::Patching::Policy::
|
33
|
+
def cs__track_join separator, ret
|
34
|
+
return ret if Contrast::Agent::Patching::Policy::Patch.skip_assess_analysis?
|
34
35
|
|
35
36
|
with_contrast_scope do
|
36
37
|
shift = 0
|
@@ -14,8 +14,6 @@ module Contrast
|
|
14
14
|
# dataflow features should be sent
|
15
15
|
# 'include Contrast::CoreExtensions::Assess::AssessExtension'
|
16
16
|
module AssessExtension
|
17
|
-
include Contrast::Utils::ScopeUtil
|
18
|
-
|
19
17
|
# Lazily build properties object. Only objects that have been tracked
|
20
18
|
# will have the @_cs__properties, but all will respond to the
|
21
19
|
# cs__properties method call. You should only call this method if you
|
@@ -1,7 +1,7 @@
|
|
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
|
-
cs__scoped_require 'contrast/
|
4
|
+
cs__scoped_require 'contrast/extensions/ruby_core/eval_trigger'
|
5
5
|
cs__scoped_require 'contrast/agent/patching/policy/patcher'
|
6
6
|
|
7
7
|
# Our patch into the BasicObject class, allowing us to track calls to the eval
|
File without changes
|
File without changes
|
@@ -50,13 +50,12 @@ class Fiber
|
|
50
50
|
def track_rb_fiber_yield fiber, _method, results
|
51
51
|
# results will be nil if StopIteration was raised,
|
52
52
|
# otherwise an Array of the yielded arguments
|
53
|
-
return
|
54
|
-
|
53
|
+
return unless results.cs__is_a?(Array)
|
55
54
|
return unless ASSESS.enabled?
|
56
55
|
|
57
56
|
with_contrast_scope do
|
58
57
|
results.each do |result|
|
59
|
-
next unless Contrast::Utils::DuckUtils.
|
58
|
+
next unless Contrast::Utils::DuckUtils.trackable?(result)
|
60
59
|
next if result.cs__frozen?
|
61
60
|
|
62
61
|
cs__splat_tags(result, fiber)
|
@@ -71,7 +70,7 @@ class Fiber
|
|
71
70
|
end
|
72
71
|
|
73
72
|
def track_rb_fiber_new fiber, _enum, _enum_method, underlying, _underlying_method
|
74
|
-
return unless Contrast::Utils::DuckUtils.
|
73
|
+
return unless Contrast::Utils::DuckUtils.trackable?(underlying)
|
75
74
|
return unless underlying.is_a?(String) && !underlying.empty?
|
76
75
|
|
77
76
|
return unless ASSESS.enabled?
|
File without changes
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
cs__scoped_require 'contrast/components/interface'
|
5
|
-
cs__scoped_require 'contrast/
|
5
|
+
cs__scoped_require 'contrast/extensions/ruby_core/assess/exec_trigger'
|
6
6
|
|
7
7
|
# This module provides us with a way to invoke Kernel propagation for those
|
8
8
|
# methods which are too complex to fit into one of the standard
|
@@ -1,7 +1,7 @@
|
|
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
|
-
cs__scoped_require 'contrast/
|
4
|
+
cs__scoped_require 'contrast/extensions/ruby_core/eval_trigger'
|
5
5
|
|
6
6
|
# Our patch into the Module class, allowing us to track calls to the eval
|
7
7
|
# method
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
cs__scoped_require 'contrast/components/interface'
|
5
|
+
# This module acts a trigger to handle the special cases of the XPath library gem
|
6
|
+
# and the Oga gem. Untrusted data may come into either of the trigger methods from
|
7
|
+
# these classes as an array or hash, respectively. Since untrusted user input comes
|
8
|
+
# into these triggers as a splat argument or an options hash, we need to iterate
|
9
|
+
# thorugh these objects to see if we were tracking on any of them and report a finding
|
10
|
+
# if so.
|
11
|
+
module XPathLibraryTrigger
|
12
|
+
include Contrast::Components::Interface
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def xpath_trigger_check context, trigger_node, _source, object, ret, invoked, *args
|
16
|
+
return ret unless args
|
17
|
+
|
18
|
+
# convert the options arg in Oga::XML::CharacterNode#initialize into an
|
19
|
+
# array of its values so we can check if any are unsafe
|
20
|
+
args = args.first.values if oga_defined? && object.cs__is_a?(Oga::XML::CharacterNode) && args.first.cs__is_a?(Hash)
|
21
|
+
|
22
|
+
args.each do |arg|
|
23
|
+
next unless arg.cs__is_a?(String) || arg.cs__is_a?(Symbol)
|
24
|
+
next unless arg.cs__tracked?
|
25
|
+
next unless trigger_node.violated?(arg)
|
26
|
+
|
27
|
+
Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(
|
28
|
+
context, trigger_node, arg, object, ret, invoked + 1, args)
|
29
|
+
end
|
30
|
+
|
31
|
+
ret
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def oga_defined?
|
37
|
+
@_oga_defined ||= defined?(Oga::XML::CharacterNode)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
File without changes
|
@@ -28,7 +28,7 @@ module Contrast
|
|
28
28
|
# source might not be all the args passed in, but it is the one we care
|
29
29
|
# about. we could pass in all the args in the last param here if it
|
30
30
|
# becomes an issue in rendering on TS
|
31
|
-
Contrast::Agent::Assess::Policy::TriggerMethod.
|
31
|
+
Contrast::Agent::Assess::Policy::TriggerMethod.apply_eval_trigger(
|
32
32
|
current_context,
|
33
33
|
trigger_node(clazz, method),
|
34
34
|
source,
|
File without changes
|
@@ -20,7 +20,7 @@ module Contrast
|
|
20
20
|
# report.
|
21
21
|
DATA_STORE_MARKER = 'data_store'
|
22
22
|
|
23
|
-
def
|
23
|
+
def patched_report_data_store _method, _exception, properties, object, _args
|
24
24
|
return unless INVENTORY.enabled?
|
25
25
|
|
26
26
|
marker = properties[DATA_STORE_MARKER]
|