Class: Contrast::Logger::CEFLog

Inherits:
Object
  • Object
show all
Includes:
Utils::CEFLogUtils, Utils::LogUtils, Singleton
Defined in:
lib/contrast/logger/cef_log.rb

Overview

This is the CEF Logger implementation. It uses the default ::Logger.

Constant Summary

Constants included from Utils::CEFLogUtils

Utils::CEFLogUtils::AGENT_VERSION, Utils::CEFLogUtils::DATE_TIME_FORMAT, Utils::CEFLogUtils::DEFAULT_CEF_NAME, Utils::CEFLogUtils::DEFAULT_LEVEL, Utils::CEFLogUtils::DEFAULT_METADATA, Utils::CEFLogUtils::EVENT_TYPE, Utils::CEFLogUtils::PROGNAME, Utils::CEFLogUtils::VALID_LEVELS

Constants included from Utils::LogUtils

Utils::LogUtils::DATE_TIME_FORMAT, Utils::LogUtils::DEFAULT_LEVEL, Utils::LogUtils::DEFAULT_NAME, Utils::LogUtils::PROGNAME, Utils::LogUtils::STDERR_STR, Utils::LogUtils::STDOUT_STR, Utils::LogUtils::VALID_LEVELS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils::LogUtils

#write_permission?

Constructor Details

#initializeCEFLog

Returns a new instance of CEFLog.



46
47
48
# File 'lib/contrast/logger/cef_log.rb', line 46

def initialize
  build_logger
end

Instance Attribute Details

#previous_levelObject (readonly)

Returns the value of attribute previous_level.



44
45
46
# File 'lib/contrast/logger/cef_log.rb', line 44

def previous_level
  @previous_level
end

#previous_pathObject (readonly)

Returns the value of attribute previous_path.



44
45
46
# File 'lib/contrast/logger/cef_log.rb', line 44

def previous_path
  @previous_path
end

Instance Method Details

#bot_blocking_message(matching_bot, outcome) ⇒ Object



123
124
125
126
# File 'lib/contrast/logger/cef_log.rb', line 123

def bot_blocking_message matching_bot, outcome
  message = "User agent #{ matching_bot[:user_agent] } matched the disallowed value #{ matching_bot[:bot] }"
  log([message, matching_bot, outcome], ::Logger::Severity::DEBUG)
end

#build_logger(log_level = nil, log_file = nil) ⇒ Object

Given new settings from TeamServer, update our logging to use the new file and level, assuming they weren’t set by local configuration.

Parameters:

  • log_level (String) (defaults to: nil)

    the level at which to log, as provided by TeamServer settings

  • log_file (String) (defaults to: nil)

    the file to which to log, as provided by TeamServer settings



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/contrast/logger/cef_log.rb', line 55

def build_logger log_level = nil, log_file = nil
  current_path_const = find_valid_path(log_file)
  current_level_const = find_valid_level(log_level)
  level_change = current_level_const != previous_level
  path_change = current_path_const != previous_path

  # don't needlessly recreate logger
  return if @cef_logger && !(level_change || path_change)

  @previous_level = current_level_const
  @previous_path = current_path_const

  @_cef_logger = build(path: current_path_const, level_const: current_level_const)
  # If we're logging to a new path, then let's start it w/ our helpful
  # data gathering messages
  # log_update if path_change
rescue StandardError => e
  # rubocop:disable Rails/Output
  if @_cef_logger
    @_cef_logger.error('Unable to process update to LoggerManager.', e)
  else
    puts('Unable to process update to LoggerManager.')
    raise(e) if ENV['CONTRAST__AGENT__RUBY_MORE_COWBELL']

    puts(e.message)
    puts(e.backtrace.join("\n"))
  end
  # rubocop:enable Rails/Output
end

#cef_loggerObject



85
86
87
# File 'lib/contrast/logger/cef_log.rb', line 85

def cef_logger
  @_cef_logger
end

#find_valid_level(log_level) ⇒ ::Ougai::Logging::Severity

Returns the level at which to log.

Returns:

  • (::Ougai::Logging::Severity)

    the level at which to log



96
97
98
99
100
101
# File 'lib/contrast/logger/cef_log.rb', line 96

def find_valid_level log_level
  config = ::Contrast::CONFIG.agent.security_logger
  config_level = config&.level&.length&.positive? ? config.level : nil

  valid_level(config_level || log_level)
end

#find_valid_path(log_file) ⇒ Object



89
90
91
92
93
# File 'lib/contrast/logger/cef_log.rb', line 89

def find_valid_path log_file
  config = ::Contrast::CONFIG.agent.security_logger
  config_path = config&.path&.length.to_i.positive? ? config.path : nil
  valid_path(config_path || log_file, default_name: DEFAULT_CEF_NAME)
end

#ineffective_attack(rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil) ⇒ Object

Log ineffective attack attack

Parameters:

  • rule_id (String)

    the rule that was triggered

  • outcome (String)

    the outcome of the rule

  • input_type (String) (defaults to: nil)

    the type of input that was detected

  • input_value (String) (defaults to: nil)

    the value of the input that was detected

  • attack_context (Contrast::Agent::RequestContext) (defaults to: nil)

    the request context of the attack



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/contrast/logger/cef_log.rb', line 161

def ineffective_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
  # We may log from the worthwatching Queue with saved attack_context
  update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
  if input_type.present? && input_value.present?
    ineffective_attack_with_input = "#{ input_type } had a value that matched a signature for, " \
                                    "but did not successfully exploit #{ rule_id } - #{ input_value }"
    log([ineffective_attack_with_input, rule_id, outcome], ::Logger::Severity::WARN)
  else
    ineffective_attack_wo_input = "An unsuccessful attack was detected against #{ rule_id }"
    log([ineffective_attack_wo_input, rule_id, outcome], ::Logger::Severity::WARN)
  end
end

#ip_denylisted_message(remote_ip, block_entry, outcome) ⇒ Object



128
129
130
131
132
# File 'lib/contrast/logger/cef_log.rb', line 128

def ip_denylisted_message remote_ip, block_entry, outcome
  message = "IP Address #{ remote_ip } matched the disallowed value" \
            "#{ block_entry[:ip] } in the IP Blacklist #{ block_entry[:uuid] }"
  log([message, block_entry, outcome], ::Logger::Severity::DEBUG)
end

#log(msg, level = @_cef_logger.level) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/contrast/logger/cef_log.rb', line 103

def log msg, level = @_cef_logger.level
  case level
  when ::Logger::Severity::INFO
    @_cef_logger.info(msg)
  when ::Logger::Severity::ERROR
    @_cef_logger.error(msg)
  when ::Logger::Severity::WARN
    @_cef_logger.warn(msg)
  when ::Logger::Severity::FATAL
    @_cef_logger.fatal(msg)
  else
    @_cef_logger.debug(msg)
  end
end

#successful_attack(rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil) ⇒ Object

Log successful attack attack

Parameters:

  • rule_id (String)

    the rule that was triggered

  • outcome (String)

    the outcome of the rule

  • input_type (String) (defaults to: nil)

    the type of input that was detected

  • input_value (String) (defaults to: nil)

    the value of the input that was detected

  • attack_context (Contrast::Agent::RequestContext) (defaults to: nil)

    the request context of the attack



141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/contrast/logger/cef_log.rb', line 141

def successful_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
  # We may log from the worthwatching Queue with saved attack_context
  update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
  if input_type.present? && input_value.present?
    successful_attack_with_input = "#{ input_type } had a value that successfully exploited" \
                                   "#{ rule_id } - #{ input_value }"
    log([successful_attack_with_input, rule_id, outcome], ::Logger::Severity::WARN)
  else
    successful_attack_wo_input = "An effective attack was detected against #{ rule_id }"
    log([successful_attack_wo_input, rule_id, outcome], ::Logger::Severity::WARN)
  end
end

#suspicious_attack(rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil) ⇒ Object

Log suspicious attack

Parameters:

  • rule_id (String)

    the rule that was triggered

  • outcome (String)

    the outcome of the rule

  • input_type (String) (defaults to: nil)

    the type of input that was detected

  • input_value (String) (defaults to: nil)

    the value of the input that was detected

  • attack_context (Contrast::Agent::RequestContext) (defaults to: nil)

    the request context of the attack



181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/contrast/logger/cef_log.rb', line 181

def suspicious_attack rule_id, outcome, input_type = nil, input_value = nil, attack_context = nil
  # We may log from the worthwatching Queue with saved attack_context
  update_logger_formatter(@_cef_logger, new_context: attack_context) if attack_context
  if input_type.present? && input_value.present?
    suspicious_attack_with = "#{ input_type } included a potential attack value that was detected" \
                             "as suspicious using #{ rule_id } - #{ input_value }"
    log([suspicious_attack_with, rule_id, outcome], ::Logger::WARN)
  elsif input_value.present?
    suspicious_attack_without = "Suspicious activity indicates a potential attack using #{ rule_id }"
    log([suspicious_attack_without, rule_id, outcome], ::Logger::WARN)
  end
end

#virtual_patch_message(patch, outcome) ⇒ Object



118
119
120
121
# File 'lib/contrast/logger/cef_log.rb', line 118

def virtual_patch_message patch, outcome
  message = "Virtual Patch #{ patch.fetch(:name, '') } - #{ patch[:uuid] } was triggered by this request."
  log([message, patch, outcome], ::Logger::Severity::DEBUG)
end