class LogStash::Codecs::CEF

Public Class Methods

new(params={}) click to toggle source
Calls superclass method
# File lib/logstash/codecs/cef.rb, line 53
def initialize(params={})
  super(params)
end

Public Instance Methods

decode(data) { |event| ... } click to toggle source
# File lib/logstash/codecs/cef.rb, line 58
def decode(data)
  # Strip any quotations at the start and end, flex connectors seem to send this
  if data[0] == "\""
    data = data[1..-2]
  end
  event = LogStash::Event.new

  # Split by the pipes
  event['cef_version'], event['cef_vendor'], event['cef_product'], event['cef_device_version'], event['cef_sigid'], event['cef_name'], event['cef_severity'], message = data.split /(?<!\\)[\|]/

  # Try and parse out the syslog header if there is one
  if event['cef_version'].include? ' '
    event['syslog'], unused, event['cef_version'] = event['cef_version'].rpartition(' ')
  end

  # Get rid of the CEF bit in the version
  version = event['cef_version'].sub /^CEF:/, ''
  event['cef_version'] = version

  # Strip any whitespace from the message
  if not message.nil? and message.include? '='
    message = message.strip

    # If the last KVP has no value, add an empty string, this prevents hash errors below
    if message.end_with?("=")
      message=message + ' '
    end

    # Now parse the key value pairs into it
    extensions = {}
    message = message.split(/ ([\w\.]+)=/)
    key, value = message.shift.split('=', 2)
    extensions[key] = value

    Hash[*message].each{ |k, v| extensions[k] = v }

    # And save the new has as the extensions
    event['cef_ext'] = extensions
  end

  yield event
end
encode(event) click to toggle source
# File lib/logstash/codecs/cef.rb, line 102
def encode(event)
  # "CEF:0|Elasticsearch|Logstash|1.0|Signature|Name|Sev|"

  vendor = sanitize_header_field(event.sprintf(@vendor))
  vendor = self.class.get_config["vendor"][:default] if vendor == ""

  product = sanitize_header_field(event.sprintf(@product))
  product = self.class.get_config["product"][:default] if product == ""

  version = sanitize_header_field(event.sprintf(@version))
  version = self.class.get_config["version"][:default] if version == ""

  signature = sanitize_header_field(event.sprintf(@signature))
  signature = self.class.get_config["signature"][:default] if signature == ""

  name = sanitize_header_field(event.sprintf(@name))
  name = self.class.get_config["name"][:default] if name == ""

  # :sev is deprecated and therefore only considered if :severity equals the default setting or is invalid
  severity = sanitize_severity(event, @severity)
  if severity == self.class.get_config["severity"][:default]
    # Use deprecated setting sev
    severity = sanitize_severity(event, @sev)
  end

  # Should also probably set the fields sent
  header = ["CEF:0", vendor, product, version, signature, name, severity].join("|")
  values = @fields.map {|fieldname| get_value(fieldname, event)}.compact.join(" ")

  @on_event.call(event, "#{header}|#{values}\n")
end

Private Instance Methods

get_value(fieldname, event) click to toggle source
# File lib/logstash/codecs/cef.rb, line 186
def get_value(fieldname, event)
  val = event[fieldname]

  return nil if val.nil?

  case val
  when Array, Hash
    return "#{sanitize_extension_key(fieldname)}=#{sanitize_extension_val(val.to_json)}"
  when LogStash::Timestamp
    return "#{sanitize_extension_key(fieldname)}=#{val.to_s}"
  else
    return "#{sanitize_extension_key(fieldname)}=#{sanitize_extension_val(val)}"
  end
end
sanitize_extension_key(value) click to toggle source

Keys must be made up of a single word, with no spaces must be alphanumeric

# File lib/logstash/codecs/cef.rb, line 159
def sanitize_extension_key(value)
  value = value.to_s.gsub(/[^a-zA-Z0-9]/, "")
  return value
end
sanitize_extension_val(value) click to toggle source

Escape equal signs in the extensions. Canonicalize newlines. CEF spec leaves it up to us to choose r or n for newline. We choose n as the default.

# File lib/logstash/codecs/cef.rb, line 167
def sanitize_extension_val(value)
  output = ""

  value = value.to_s.gsub(/\r\n/, "\n")

  value.each_char{|c|
    case c
    when "\\", "="
      output += "\\" + c
    when "\n", "\r"
      output += "\\n"
    else
      output += c
    end
  }

  return output
end
sanitize_header_field(value) click to toggle source

Escape pipes and backslashes in the header. Equal signs are ok. Newlines are forbidden.

# File lib/logstash/codecs/cef.rb, line 138
def sanitize_header_field(value)
  output = ""

  value = value.to_s.gsub(/\r\n/, "\n")

  value.each_char{|c|
    case c
    when "\\", "|"
      output += "\\" + c
    when "\n", "\r"
      output += " "
    else
      output += c
    end
  }

  return output
end
sanitize_severity(event, severity) click to toggle source
# File lib/logstash/codecs/cef.rb, line 201
def sanitize_severity(event, severity)
  severity = sanitize_header_field(event.sprintf(severity)).strip
  severity = self.class.get_config["severity"][:default] unless valid_severity?(severity)
  severity = severity.to_i.to_s
end
valid_severity?(sev) click to toggle source
# File lib/logstash/codecs/cef.rb, line 207
def valid_severity?(sev)
  f = Float(sev)
  # check if it's an integer or a float with no remainder
  # and if the value is between 0 and 10 (inclusive)
  (f % 1 == 0) && f.between?(0,10)
rescue TypeError, ArgumentError
  false
end