contrast-agent 5.1.0 → 5.2.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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_kernel/cs__assess_kernel.c +7 -4
  3. data/ext/cs__assess_module/cs__assess_module.c +7 -7
  4. data/ext/cs__common/cs__common.c +4 -0
  5. data/ext/cs__common/cs__common.h +1 -0
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +52 -27
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +2 -0
  8. data/ext/cs__scope/cs__scope.c +747 -0
  9. data/ext/cs__scope/cs__scope.h +88 -0
  10. data/ext/cs__scope/extconf.rb +5 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +20 -13
  12. data/lib/contrast/agent/assess/contrast_object.rb +4 -1
  13. data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -5
  14. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +2 -0
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -1
  16. data/lib/contrast/agent/assess/rule/response/{autocomplete_rule.rb → auto_complete_rule.rb} +4 -3
  17. data/lib/contrast/agent/assess/rule/response/base_rule.rb +12 -79
  18. data/lib/contrast/agent/assess/rule/response/body_rule.rb +109 -0
  19. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +157 -0
  20. data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +26 -0
  21. data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +14 -15
  22. data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +5 -25
  23. data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +29 -0
  24. data/lib/contrast/agent/assess/rule/response/header_rule.rb +70 -0
  25. data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +12 -36
  26. data/lib/contrast/agent/assess/rule/response/parameters_pollution_rule.rb +2 -1
  27. data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +26 -0
  28. data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +36 -0
  29. data/lib/contrast/agent/middleware.rb +1 -0
  30. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +1 -3
  31. data/lib/contrast/agent/patching/policy/patch.rb +2 -6
  32. data/lib/contrast/agent/patching/policy/patcher.rb +1 -1
  33. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +94 -0
  34. data/lib/contrast/agent/protect/rule/base.rb +28 -1
  35. data/lib/contrast/agent/protect/rule/base_service.rb +10 -1
  36. data/lib/contrast/agent/protect/rule/cmd_injection.rb +2 -0
  37. data/lib/contrast/agent/protect/rule/deserialization.rb +6 -0
  38. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +5 -1
  39. data/lib/contrast/agent/protect/rule/no_sqli.rb +1 -0
  40. data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -0
  41. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +124 -0
  42. data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +121 -0
  43. data/lib/contrast/agent/protect/rule/sqli.rb +33 -0
  44. data/lib/contrast/agent/protect/rule/xxe.rb +4 -0
  45. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +44 -0
  46. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +115 -0
  47. data/lib/contrast/agent/reporting/input_analysis/input_type.rb +44 -0
  48. data/lib/contrast/agent/reporting/input_analysis/score_level.rb +21 -0
  49. data/lib/contrast/agent/reporting/report.rb +1 -0
  50. data/lib/contrast/agent/reporting/reporter.rb +8 -1
  51. data/lib/contrast/agent/reporting/reporting_events/finding.rb +69 -36
  52. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +88 -59
  53. data/lib/contrast/agent/reporting/reporting_events/{finding_object.rb → finding_event_object.rb} +24 -20
  54. data/lib/contrast/agent/reporting/reporting_events/finding_event_parent_object.rb +39 -0
  55. data/lib/contrast/agent/reporting/reporting_events/finding_event_property.rb +40 -0
  56. data/lib/contrast/agent/reporting/reporting_events/{finding_signature.rb → finding_event_signature.rb} +29 -24
  57. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +12 -8
  58. data/lib/contrast/agent/reporting/reporting_events/{finding_stack.rb → finding_event_stack.rb} +23 -19
  59. data/lib/contrast/agent/reporting/reporting_events/{finding_taint_range.rb → finding_event_taint_range.rb} +17 -15
  60. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +26 -53
  61. data/lib/contrast/agent/reporting/reporting_events/poll.rb +29 -0
  62. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +5 -4
  63. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +1 -0
  64. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +1 -1
  65. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +10 -3
  66. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +0 -1
  67. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -0
  68. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +28 -20
  69. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
  70. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +13 -1
  71. data/lib/contrast/agent/request_context.rb +6 -1
  72. data/lib/contrast/agent/request_context_extend.rb +85 -21
  73. data/lib/contrast/agent/scope.rb +102 -107
  74. data/lib/contrast/agent/service_heartbeat.rb +45 -2
  75. data/lib/contrast/agent/version.rb +1 -1
  76. data/lib/contrast/api/decorators/bot_blocker.rb +37 -0
  77. data/lib/contrast/api/decorators/ip_denylist.rb +37 -0
  78. data/lib/contrast/api/decorators/rasp_rule_sample.rb +29 -0
  79. data/lib/contrast/api/decorators/user_input.rb +11 -1
  80. data/lib/contrast/api/decorators/virtual_patch.rb +34 -0
  81. data/lib/contrast/components/logger.rb +5 -0
  82. data/lib/contrast/components/protect.rb +4 -2
  83. data/lib/contrast/components/scope.rb +98 -91
  84. data/lib/contrast/config/agent_configuration.rb +58 -12
  85. data/lib/contrast/config/api_configuration.rb +100 -12
  86. data/lib/contrast/config/api_proxy_configuration.rb +55 -3
  87. data/lib/contrast/config/application_configuration.rb +114 -15
  88. data/lib/contrast/config/assess_configuration.rb +106 -12
  89. data/lib/contrast/config/assess_rules_configuration.rb +44 -3
  90. data/lib/contrast/config/base_configuration.rb +1 -0
  91. data/lib/contrast/config/certification_configuration.rb +74 -3
  92. data/lib/contrast/config/exception_configuration.rb +61 -3
  93. data/lib/contrast/config/heap_dump_configuration.rb +101 -17
  94. data/lib/contrast/config/inventory_configuration.rb +64 -3
  95. data/lib/contrast/config/logger_configuration.rb +46 -3
  96. data/lib/contrast/config/protect_rule_configuration.rb +36 -9
  97. data/lib/contrast/config/protect_rules_configuration.rb +120 -17
  98. data/lib/contrast/config/request_audit_configuration.rb +68 -3
  99. data/lib/contrast/config/ruby_configuration.rb +96 -22
  100. data/lib/contrast/config/sampling_configuration.rb +76 -10
  101. data/lib/contrast/config/server_configuration.rb +56 -11
  102. data/lib/contrast/configuration.rb +6 -3
  103. data/lib/contrast/logger/cef_log.rb +151 -0
  104. data/lib/contrast/utils/hash_digest.rb +14 -6
  105. data/lib/contrast/utils/log_utils.rb +114 -0
  106. data/lib/contrast/utils/middleware_utils.rb +6 -7
  107. data/lib/contrast/utils/net_http_base.rb +12 -9
  108. data/lib/contrast/utils/patching/policy/patch_utils.rb +0 -4
  109. data/lib/contrast.rb +4 -3
  110. data/ruby-agent.gemspec +1 -1
  111. data/service_executables/VERSION +1 -1
  112. data/service_executables/linux/contrast-service +0 -0
  113. data/service_executables/mac/contrast-service +0 -0
  114. metadata +41 -21
  115. data/lib/contrast/agent/assess/rule/response/cachecontrol_rule.rb +0 -184
  116. data/lib/contrast/agent/assess/rule/response/clickjacking_rule.rb +0 -66
  117. data/lib/contrast/agent/assess/rule/response/x_content_type_rule.rb +0 -52
  118. data/lib/contrast/agent/assess/rule/response/x_xss_protection_rule.rb +0 -53
  119. data/lib/contrast/extension/kernel.rb +0 -54
@@ -1,184 +0,0 @@
1
- # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'contrast/agent/assess/rule/response/base_rule'
5
- require 'contrast/utils/string_utils'
6
- require 'json'
7
-
8
- module Contrast
9
- module Agent
10
- module Assess
11
- module Rule
12
- module Response
13
- # These rules check the content of the HTTP Response to determine if the body or the headers include and/or
14
- # set incorrectly the cache-control header
15
- class Cachecontrol < BaseRule
16
- def rule_id
17
- 'cache-controls-missing'
18
- end
19
-
20
- protected
21
-
22
- HEADER_KEY = 'Cache-Control'.cs__freeze
23
- ACCEPTED_VALUES = %w[no-store no-cache].cs__freeze
24
-
25
- # Rules discern which responses they can/should analyze.
26
- #
27
- # @param response [Contrast::Agent::Response] the response of the application
28
- def analyze_response? response
29
- super && body?(response)
30
- end
31
-
32
- # Determine if the Response violates the Rule or not. If it does, return the evidence that proves it so.
33
- #
34
- # @param response [Contrast::Agent::Response] the response of the application
35
- # @return [Hash, nil] the evidence required to prove the violation of the rule
36
- def violated? response
37
- # This rule is violated if the header is not there
38
- # or if it's there, but the value is not 'no-store' or 'no-cache'
39
- headers = response.headers
40
- header_key_sym = HEADER_KEY.to_sym
41
- cache_control = headers[HEADER_KEY] || headers[header_key_sym]
42
- if cache_control && !valid_header?(cache_control)
43
- return to_cachecontrol_rule('header', 'cache-control', cache_control)
44
- end
45
-
46
- body = response.body
47
- # check if the meta tag is include it
48
- tags = meta_tags(body)
49
-
50
- tags.each do |tag|
51
- return to_cachecontrol_rule('meta', 'pragma', tag[HTML_PROP]) if meta_cache_tag? tag[HTML_PROP]
52
- end
53
- # we should return if header not presented and no tags are detected
54
- return {} if !(headers.key?(HEADER_KEY) || headers.key?(header_key_sym)) && tags.empty?
55
-
56
- nil
57
- end
58
-
59
- def valid_header? header
60
- ACCEPTED_VALUES.any? { |val| header.include?(val) || header == val }
61
- end
62
-
63
- # Find the tags in this body, if any, so as to determine if they violate this rule.
64
- #
65
- # @param body [String,nil]
66
- # @return [Array<Hash>] the tags of this body, as well as their start and end indexes.
67
- def meta_tags body
68
- tags = []
69
- body_start = 0
70
-
71
- # meta tags are stored in the <head></head> section
72
- head_section = body&.split(head_tag)
73
- return [] unless head_section
74
-
75
- potential_tags = head_section.map { |el| el.split(meta_start) }
76
- potential_tags.flatten.each do |potential_tag|
77
- next unless potential_tag
78
- next unless tag_openings.any? { |opening| potential_tag.starts_with?(opening) }
79
-
80
- body_start = body.index(meta_start, body_start)
81
- next unless body_start
82
-
83
- tag_stop = potential_tag.index('>').to_i
84
- next unless tag_stop
85
-
86
- body_close = body_start + 6 + tag_stop
87
- tags << capture(body, body_start, body_close, tag_stop)
88
- body_start = body_close
89
- end
90
- tags
91
- end
92
-
93
- def meta_start
94
- /<meta/i
95
- end
96
-
97
- def head_tag
98
- /<head>/i
99
- end
100
-
101
- def tag_openings
102
- [' ', "\n", "\r", "\t"]
103
- end
104
-
105
- def accepted_http_values
106
- [/'cache-control'/i, /"cache-control"/i]
107
- end
108
-
109
- def accepted_values
110
- [/'no-cache'/i, /"no-cache"/i, /"no-store"/i, /'no-store'/i]
111
- end
112
-
113
- # Determine if the given metatag does not have a valid cache-control tag.
114
- # Meta tags has the option to set http-equiv and content to set the http response header
115
- # to define for the document
116
- #
117
- # @param tag [String] the meta tag
118
- # @return [Boolean, nil]
119
- def meta_cache_tag? tag
120
- # Here we should determine the index of the needed keys
121
- # http-equiv and content
122
- http_equiv_idx = tag =~ /http-equiv=/i
123
- return false unless http_equiv_idx
124
-
125
- content_idx = tag =~ /content=/i
126
- return false unless content_idx
127
-
128
- # determine the value of the http-equiv if it's cache-control
129
- http_equiv_idx += 11
130
- is_valid = accepted_http_values.any? { |el| (tag =~ el) == http_equiv_idx }
131
- return false unless is_valid
132
-
133
- content_idx += 8
134
- return false if accepted_values.any? { |value| (tag =~ value) == content_idx }
135
-
136
- true
137
- end
138
-
139
- # This method allows to change the evidence we attach and the way we attach it
140
- # Change it accordingly the rule you work on
141
- #
142
- # @param evidence [Hash] the properties required to build this finding.
143
- # @param finding [Contrast::Api::Dtm::Finding] finding to attach the evidence to
144
- def build_evidence evidence, finding
145
- evidence.each_pair do |key, value|
146
- finding.properties[key] = Contrast::Utils::StringUtils.protobuf_format(value)
147
- end
148
- end
149
-
150
- # This method accepts the violation and transforms it to the proper hash
151
- # before return in as violation
152
- #
153
- # @param type [String] String of Header or META of the type
154
- # @param name [String] String of either cache-control or pragma
155
- # @param value [String] String of the violated value
156
- def to_cachecontrol_rule type, name, value
157
- { data: { type: type, name: name, value: value }.to_s }
158
- end
159
-
160
- # Capture the information needed to build the properties of this finding by parsing out from the body
161
- #
162
- # @param body [String] the entire HTTP Response body
163
- # @param body_start [Integer] the start of the range to take from the body
164
- # @param body_close [Integer] the end of the range to take from the body
165
- # @param tag_stop [Integer] the index of the end of the html tag from its start
166
- # @return [Hash]
167
- def capture body, body_start, body_close, tag_stop
168
- # In this situation we don't need to capture before and after the meta tag, as this may produce an error
169
- # So if we capture 30-50 chars before and after the tag, we may capture part of the tag, we want to
170
- # inspect and eventually this wil return wrong string. Because of that - we split the <head> and take
171
- # each meta tag and examine it
172
- tag = {}
173
- # we dont need to capture here before or after the meta tag
174
- tag[HTML_PROP] = body[body_start...body_close]
175
- tag[START_PROP] = body_start
176
- tag[END_PROP] = tag[START_PROP] + 6 + tag_stop
177
- tag
178
- end
179
- end
180
- end
181
- end
182
- end
183
- end
184
- end
@@ -1,66 +0,0 @@
1
- # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'contrast/agent/assess/rule/response/base_rule'
5
- require 'contrast/utils/string_utils'
6
-
7
- module Contrast
8
- module Agent
9
- module Assess
10
- module Rule
11
- module Response
12
- # These rules check the content of the HTTP Response to determine if the headers contains the required header
13
- class Clickjacking < BaseRule
14
- def rule_id
15
- 'clickjacking-control-missing'
16
- end
17
-
18
- protected
19
-
20
- HEADER_KEY = 'X-Frame-Options'.cs__freeze
21
- HEADER_KEY_SYM = HEADER_KEY.to_sym
22
- ACCEPTED_VALUES = [/^deny/i, /^sameorigin/i].cs__freeze
23
-
24
- # Rules discern which responses they can/should analyze.
25
- #
26
- # @param response [Contrast::Agent::Response] the response of the application
27
- def analyze_response? response
28
- super && headers?(response)
29
- end
30
-
31
- # Determine if the Response violates the Rule or not. If it does, return the evidence that proves it so.
32
- #
33
- # @param response [Contrast::Agent::Response] the response of the application
34
- # @return [Hash, nil] the evidence required to prove the violation of the rule
35
- def violated? response
36
- headers = response.headers
37
- cache_control = headers[HEADER_KEY] || headers[HEADER_KEY_SYM]
38
- return unsafe_response unless cache_control
39
- return unsafe_response(cache_control) unless valid_header?(cache_control)
40
-
41
- nil
42
- end
43
-
44
- def valid_header? header
45
- ACCEPTED_VALUES.any? { |val| header =~ val }
46
- end
47
-
48
- def unsafe_response value = ''
49
- { data: value }
50
- end
51
-
52
- # Change it accordingly the rule you work on
53
- #
54
- # @param evidence [Hash] the properties required to build this finding.
55
- # @param finding [Contrast::Api::Dtm::Finding] finding to attach the evidence to
56
- def build_evidence evidence, finding
57
- evidence.each_pair do |key, value|
58
- finding.properties[key] = Contrast::Utils::StringUtils.protobuf_format(value)
59
- end
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,52 +0,0 @@
1
- # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'contrast/agent/assess/rule/response/base_rule'
5
- require 'contrast/utils/string_utils'
6
-
7
- module Contrast
8
- module Agent
9
- module Assess
10
- module Rule
11
- module Response
12
- # These rules check the content of the HTTP Response to determine if the response contains the needed header
13
- class XContentType < BaseRule
14
- def rule_id
15
- 'xcontenttype-header-missing'
16
- end
17
-
18
- protected
19
-
20
- HEADER_KEY = 'X-Content-Type-Options'.cs__freeze
21
- HEADER_KEY_SYM = HEADER_KEY.to_sym
22
- ACCEPTED_VALUE = /^nosniff/i.cs__freeze
23
-
24
- # Rules discern which responses they can/should analyze.
25
- #
26
- # @param response [Contrast::Agent::Response] the response of the application
27
- def analyze_response? response
28
- super && headers?(response)
29
- end
30
-
31
- # Determine if the Response violates the Rule or not. If it does, return the evidence that proves it so.
32
- #
33
- # @param response [Contrast::Agent::Response] the response of the application
34
- # @return [Hash, nil] the evidence required to prove the violation of the rule
35
- def violated? response
36
- headers = response.headers
37
- x_content_type = headers[HEADER_KEY] || headers[HEADER_KEY_SYM]
38
- return unsafe_response unless x_content_type
39
- return unsafe_response x_content_type unless ACCEPTED_VALUE.match?(x_content_type)
40
-
41
- nil
42
- end
43
-
44
- def unsafe_response value = ''
45
- { data: value }
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,53 +0,0 @@
1
- # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'contrast/agent/assess/rule/response/base_rule'
5
- require 'contrast/utils/string_utils'
6
-
7
- module Contrast
8
- module Agent
9
- module Assess
10
- module Rule
11
- module Response
12
- # These rules check the content of the HTTP Response to determine if the response contains the needed header
13
- class XXssProtection < BaseRule
14
- def rule_id
15
- 'xxssprotection-header-disabled'
16
- end
17
-
18
- protected
19
-
20
- HEADER_KEY = 'X-XSS-Protection'.cs__freeze
21
- HEADER_KEY_SYM = HEADER_KEY.to_sym
22
- ACCEPTED_VALUE = /^1/.cs__freeze
23
-
24
- # Rules discern which responses they can/should analyze.
25
- #
26
- # @param response [Contrast::Agent::Response] the response of the application
27
- def analyze_response? response
28
- super && headers?(response)
29
- end
30
-
31
- # Determine if the Response violates the Rule or not. If it does, return the evidence that proves it so.
32
- #
33
- # @param response [Contrast::Agent::Response] the response of the application
34
- # @return [Hash, nil] the evidence required to prove the violation of the rule
35
- def violated? response
36
- headers = response.headers
37
- x_xss_protection = headers[HEADER_KEY] || headers[HEADER_KEY_SYM]
38
- # header is safe by default so only need to return finding on failed value match
39
- return unless x_xss_protection
40
- return unsafe_response x_xss_protection unless ACCEPTED_VALUE.match?(x_xss_protection)
41
-
42
- nil
43
- end
44
-
45
- def unsafe_response value = ''
46
- { data: value }
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,54 +0,0 @@
1
- # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: true
3
-
4
- require 'contrast/components/scope'
5
-
6
- # This is a reasonable place for the Kernel#catch hook to live.
7
- # No current plans for component re-design, but if we had some kind of
8
- # "do this when a component is hooked in" thing, this would live there.
9
- # For now, it's over-engineering to live anywhere else. -ajm
10
- module Kernel # :nodoc:
11
- alias_method :cs__catch, :catch
12
-
13
- # In the event of a `throw`, we need to override `catch`
14
- # to save & restore scope state:
15
- #
16
- # scope_level == 0
17
- #
18
- # catch(:abc) do
19
- # with_contrast_scope do
20
- # throw :abc # will leak
21
- # end
22
- # end
23
- #
24
- # scope_level == 1
25
- #
26
- # Frankly, this isn't how scope should be used. This is in place of
27
- # proper `ensure` blocks within the instrumentation call stack.
28
- # This will actually /create/ scope leaks if you're doing something like:
29
- #
30
- # catch(:ohno) do
31
- # enter scope
32
- # end
33
- #
34
- # abc()
35
- #
36
- # exit scope
37
- #
38
- # i.e. if you intend to change net scope across a catch block boundary.
39
-
40
- private
41
-
42
- def catch *args, &block
43
- # Save current scope level
44
- scope_level = ::Contrast::SCOPE.scope_for_current_ec.instance_variable_get(:@contrast_scope)
45
-
46
- # Run original catch with block.
47
- retval = cs__catch(*args, &block)
48
-
49
- # Restore scope.
50
- ::Contrast::SCOPE.scope_for_current_ec.instance_variable_set(:@contrast_scope, scope_level)
51
-
52
- retval
53
- end
54
- end