contrast-agent 7.4.0 → 7.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/hooks/at_exit_hook.rb +16 -1
  3. data/lib/contrast/agent/middleware/middleware.rb +1 -1
  4. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +19 -12
  5. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +55 -20
  6. data/lib/contrast/agent/protect/policy/rule_applicator.rb +1 -4
  7. data/lib/contrast/agent/protect/rule/base.rb +56 -25
  8. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker.rb +12 -4
  9. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker_input_classification.rb +0 -26
  10. data/lib/contrast/agent/protect/rule/cmdi/cmd_injection.rb +2 -5
  11. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +2 -4
  12. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +2 -1
  13. data/lib/contrast/agent/protect/rule/deserialization/deserialization.rb +4 -4
  14. data/lib/contrast/agent/protect/rule/input_classification/base.rb +1 -4
  15. data/lib/contrast/agent/protect/rule/input_classification/encoding.rb +34 -2
  16. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli.rb +5 -2
  17. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal.rb +12 -7
  18. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_semantic_security_bypass.rb +2 -2
  19. data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +2 -3
  20. data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +3 -4
  21. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload.rb +3 -0
  22. data/lib/contrast/agent/protect/rule/utils/builders.rb +3 -4
  23. data/lib/contrast/agent/protect/rule/utils/filters.rb +32 -16
  24. data/lib/contrast/agent/protect/rule/xss/xss.rb +80 -0
  25. data/lib/contrast/agent/protect/rule/xxe/xxe.rb +9 -2
  26. data/lib/contrast/agent/reporting/details/xss_match.rb +17 -0
  27. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +32 -0
  28. data/lib/contrast/agent/reporting/input_analysis/input_type.rb +4 -34
  29. data/lib/contrast/agent/reporting/reporting_events/finding.rb +1 -5
  30. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -5
  31. data/lib/contrast/agent/reporting/reporting_utilities/build_preflight.rb +4 -4
  32. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +5 -1
  33. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
  34. data/lib/contrast/agent/request/request_context_extend.rb +0 -2
  35. data/lib/contrast/agent/version.rb +1 -1
  36. data/lib/contrast/components/assess.rb +4 -0
  37. data/lib/contrast/framework/rails/support.rb +2 -2
  38. data/lib/contrast/logger/cef_log.rb +30 -4
  39. data/lib/contrast/utils/io_util.rb +3 -0
  40. data/lib/contrast/utils/json.rb +1 -1
  41. data/lib/contrast/utils/log_utils.rb +21 -10
  42. data/ruby-agent.gemspec +3 -2
  43. metadata +18 -12
@@ -52,8 +52,6 @@ module Contrast
52
52
  if (ia = Contrast::Agent::Protect::InputAnalyzer.analyse(request))
53
53
  # Handle prefilter
54
54
  Contrast::Agent::Protect::InputAnalyzer.input_classification(ia, prefilter: true)
55
- # Reflected xss infilter
56
- Contrast::Agent::Protect::InputAnalyzer.input_classification_for('reflected-xss', ia)
57
55
  @agent_input_analysis = ia
58
56
  else
59
57
  logger.trace('Analysis from Agent was empty.')
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '7.4.0'
6
+ VERSION = '7.5.0'
7
7
  end
8
8
  end
@@ -237,6 +237,10 @@ module Contrast
237
237
 
238
238
  # The id for this process, based on the session metadata or id provided by the user, as indicated in
239
239
  # application startup.
240
+ #
241
+ # The ID of the current application run, as returned by the application settings endpoint or set by
242
+ # application.session_id. If there is no session associated with this run, this field should be omitted
243
+ # when reporting to TS.
240
244
  def session_id
241
245
  ::Contrast::SETTINGS.assess_state.session_id
242
246
  end
@@ -59,7 +59,7 @@ module Contrast
59
59
  # ActionDispatch::Journey::Path::Pattern::MatchData, Hash, ActionDispatch::Journey::Route, Array<String>
60
60
  match, _params, route, path = get_full_route(request.rack_request)
61
61
  unless route
62
- logger.warn("Unable to determine the current route of this request: #{ request.rack_request }")
62
+ logger.debug("Unable to determine the current route of this request: #{ request.rack_request }")
63
63
  return
64
64
  end
65
65
 
@@ -77,7 +77,7 @@ module Contrast
77
77
  new_route_coverage&.attach_rails_data(route, original_url)
78
78
  new_route_coverage
79
79
  rescue StandardError => e
80
- logger.warn('Unable to determine the current route of this request due to exception: ', e)
80
+ logger.error('Unable to determine the current route of this request due to exception: ', e)
81
81
  nil
82
82
  end
83
83
 
@@ -131,7 +131,16 @@ module Contrast
131
131
  log([message, block_entry, outcome], ::Logger::Severity::DEBUG)
132
132
  end
133
133
 
134
- def successful_attack rule_id, outcome, input_type = nil, input_value = nil
134
+ # Log successful attack attack
135
+ #
136
+ # @param rule_id [String] the rule that was triggered
137
+ # @param outcome [String] the outcome of the rule
138
+ # @param input_type [String] the type of input that was detected
139
+ # @param input_value [String] the value of the input that was detected
140
+ # @param attack_context [Contrast::Agent::RequestContext] the request context of the attack
141
+ def successful_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
142
+ # We may log from the worthwatching Queue with saved attack_context
143
+ update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
135
144
  if input_type.present? && input_value.present?
136
145
  successful_attack_with_input = "#{ input_type } had a value that successfully exploited" \
137
146
  "#{ rule_id } - #{ input_value }"
@@ -142,7 +151,16 @@ module Contrast
142
151
  end
143
152
  end
144
153
 
145
- def ineffective_attack rule_id, outcome, input_type = nil, input_value = nil
154
+ # Log ineffective attack attack
155
+ #
156
+ # @param rule_id [String] the rule that was triggered
157
+ # @param outcome [String] the outcome of the rule
158
+ # @param input_type [String] the type of input that was detected
159
+ # @param input_value [String] the value of the input that was detected
160
+ # @param attack_context [Contrast::Agent::RequestContext] the request context of the attack
161
+ def ineffective_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
162
+ # We may log from the worthwatching Queue with saved attack_context
163
+ update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
146
164
  if input_type.present? && input_value.present?
147
165
  ineffective_attack_with_input = "#{ input_type } had a value that matched a signature for, " \
148
166
  "but did not successfully exploit #{ rule_id } - #{ input_value }"
@@ -153,8 +171,16 @@ module Contrast
153
171
  end
154
172
  end
155
173
 
156
- # newer - currently not in the agent, currently is a probe for us
157
- def suspicious_attack rule_id, outcome, input_type = nil, input_value = nil
174
+ # Log suspicious attack
175
+ #
176
+ # @param rule_id [String] the rule that was triggered
177
+ # @param outcome [String] the outcome of the rule
178
+ # @param input_type [String] the type of input that was detected
179
+ # @param input_value [String] the value of the input that was detected
180
+ # @param attack_context [Contrast::Agent::RequestContext] the request context of the attack
181
+ def suspicious_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
182
+ # We may log from the worthwatching Queue with saved attack_context
183
+ update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
158
184
  if input_type.present? && input_value.present?
159
185
  suspicious_attack_with = "#{ input_type } included a potential attack value that was detected" \
160
186
  "as suspicious using #{ rule_id } - #{ input_value }"
@@ -7,6 +7,8 @@ module Contrast
7
7
  module Utils
8
8
  # Util for information about an IO
9
9
  module IOUtil
10
+ UNKNOWN_IO = 'unknown'
11
+
10
12
  extend Contrast::Components::Logger::InstanceMethods
11
13
 
12
14
  class << self
@@ -48,6 +50,7 @@ module Contrast
48
50
  return false unless status
49
51
  return false if status.pipe?
50
52
  return false if status.socket?
53
+ return false if status.ftype == UNKNOWN_IO
51
54
 
52
55
  true
53
56
  end
@@ -14,7 +14,7 @@ module Contrast
14
14
 
15
15
  # Add any known cases where parsing error might arise from older json parser:
16
16
  # @return [Array<String>]
17
- SPECIAL_CASES = ["\"\""].cs__freeze # rubocop:disable Style/StringLiterals
17
+ SPECIAL_CASES = ["\"\"", "\"0\""].cs__freeze # rubocop:disable Style/StringLiterals
18
18
 
19
19
  # Parses a string using JSON.parser. This method is used instead of standard JSON.parse to
20
20
  # support older versions of json gem => not supporting key-value second parameter, which is
@@ -146,6 +146,7 @@ module Contrast
146
146
  private
147
147
 
148
148
  def build path: STDOUT_STR, level_const: DEFAULT_LEVEL
149
+ context = Contrast::Agent::REQUEST_TRACKER.current
149
150
  logger = case path
150
151
  when STDOUT_STR, STDERR_STR
151
152
  ::Logger.new(Object.cs__const_get(path))
@@ -154,15 +155,23 @@ module Contrast
154
155
  end
155
156
  logger.progname = PROGNAME
156
157
  logger.level = level_const
157
- change_logger_formatter(logger)
158
+ update_logger_formatter(logger, new_context: context)
158
159
  logger
159
160
  end
160
161
 
161
162
  def context
162
- Contrast::Agent::REQUEST_TRACKER.current
163
+ @_context ||= Contrast::Agent::REQUEST_TRACKER.current
163
164
  end
164
165
 
165
- def change_logger_formatter logger
166
+ def context_update new_context
167
+ @_context = new_context unless new_context.nil?
168
+ end
169
+
170
+ # @param logger [Logger]
171
+ # @param new_context [Contrast::Agent::RequestContext]
172
+ def update_logger_formatter logger, new_context: nil
173
+ context_update(new_context) if new_context
174
+
166
175
  ip_address = extract_ip_address
167
176
  logger.formatter = proc do |severity, datetime, progname, msg|
168
177
  date_format = datetime.strftime(DATE_TIME_FORMAT)
@@ -206,7 +215,7 @@ module Contrast
206
215
  request_method = assign_request_method(context)
207
216
  app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
208
217
  attach_request_and_sender_info(message, sender_info)
209
- message << "request=#{ context.request.url } "
218
+ message << "request=#{ context&.request&.url } "
210
219
  message << "requestMethod=#{ request_method } "
211
220
  message << "app=#{ app_name } "
212
221
  message << "outcome=#{ outcome } "
@@ -238,16 +247,18 @@ module Contrast
238
247
  end
239
248
 
240
249
  def extract_sender_ip
241
- request_headers = context.activity.request.headers&.transform_keys(&:to_s)
250
+ request_headers = context&.activity&.request&.headers&.transform_keys(&:to_s)
251
+ return unless request_headers
252
+
242
253
  request_headers['X-Forwarded-For']
243
254
  end
244
255
 
245
256
  def assign_request_method context
246
- if context.request.rack_request.env['REQUEST_METHOD'].length.positive?
247
- context.request.rack_request.env['REQUEST_METHOD']
248
- else
249
- DEFAULT_METADATA
250
- end
257
+ request_method = context&.request&.rack_request&.env
258
+ request_method = request_method['REQUEST_METHOD'] if request_method
259
+ return DEFAULT_METADATA if request_method.nil? || !request_method.length.positive?
260
+
261
+ request_method
251
262
  end
252
263
  end
253
264
  end
data/ruby-agent.gemspec CHANGED
@@ -116,9 +116,10 @@ end
116
116
  # dependencies.csv in this directory to indicate that and create a
117
117
  # corresponding update to the fake gem server data in TeamServer.
118
118
  def self.add_dependencies spec
119
- spec.add_dependency 'ffi', '~> 1.0'
119
+ # TODO: RUBY-99999 investigate init_with_options segmentation fault
120
+ spec.add_dependency 'ffi'
120
121
  spec.add_dependency 'ougai', '>= 1.8', '< 3.0.0'
121
- spec.add_dependency 'rack', '~> 2.0'
122
+ spec.add_dependency 'rack', '>= 2.0', '< 4.0.0'
122
123
 
123
124
  # bind this directly as we've had issues w/ build changes on bug release
124
125
  spec.add_dependency 'contrast-agent-lib', '1.1.1'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contrast-agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.4.0
4
+ version: 7.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - galen.palmer@contrastsecurity.com
@@ -10,10 +10,10 @@ authors:
10
10
  - alex.macdonald@contrastsecurity.com
11
11
  - mark.petersen@contrastsecurity.com
12
12
  - joshua.reed@contrastsecurity.com
13
- autorequire:
13
+ autorequire:
14
14
  bindir: exe
15
15
  cert_chain: []
16
- date: 2023-09-12 00:00:00.000000000 Z
16
+ date: 2023-10-06 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: bundler
@@ -619,16 +619,16 @@ dependencies:
619
619
  name: ffi
620
620
  requirement: !ruby/object:Gem::Requirement
621
621
  requirements:
622
- - - "~>"
622
+ - - ">="
623
623
  - !ruby/object:Gem::Version
624
- version: '1.0'
624
+ version: '0'
625
625
  type: :runtime
626
626
  prerelease: false
627
627
  version_requirements: !ruby/object:Gem::Requirement
628
628
  requirements:
629
- - - "~>"
629
+ - - ">="
630
630
  - !ruby/object:Gem::Version
631
- version: '1.0'
631
+ version: '0'
632
632
  - !ruby/object:Gem::Dependency
633
633
  name: ougai
634
634
  requirement: !ruby/object:Gem::Requirement
@@ -653,16 +653,22 @@ dependencies:
653
653
  name: rack
654
654
  requirement: !ruby/object:Gem::Requirement
655
655
  requirements:
656
- - - "~>"
656
+ - - ">="
657
657
  - !ruby/object:Gem::Version
658
658
  version: '2.0'
659
+ - - "<"
660
+ - !ruby/object:Gem::Version
661
+ version: 4.0.0
659
662
  type: :runtime
660
663
  prerelease: false
661
664
  version_requirements: !ruby/object:Gem::Requirement
662
665
  requirements:
663
- - - "~>"
666
+ - - ">="
664
667
  - !ruby/object:Gem::Version
665
668
  version: '2.0'
669
+ - - "<"
670
+ - !ruby/object:Gem::Version
671
+ version: 4.0.0
666
672
  - !ruby/object:Gem::Dependency
667
673
  name: contrast-agent-lib
668
674
  requirement: !ruby/object:Gem::Requirement
@@ -1370,7 +1376,7 @@ metadata:
1370
1376
  support_uri: https://support.contrastsecurity.com
1371
1377
  trouble_shooting_uri: https://support.contrastsecurity.com/hc/en-us/search?utf8=%E2%9C%93&query=Ruby
1372
1378
  wiki_uri: https://docs.contrastsecurity.com/
1373
- post_install_message:
1379
+ post_install_message:
1374
1380
  rdoc_options: []
1375
1381
  require_paths:
1376
1382
  - lib
@@ -1388,8 +1394,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1388
1394
  - !ruby/object:Gem::Version
1389
1395
  version: '0'
1390
1396
  requirements: []
1391
- rubygems_version: 3.2.33
1392
- signing_key:
1397
+ rubygems_version: 3.3.26
1398
+ signing_key:
1393
1399
  specification_version: 4
1394
1400
  summary: Contrast Security's agent for rack-based applications.
1395
1401
  test_files: []