module Sqreen::Signals::Conversions

Public Class Methods

convert_attack(attack) click to toggle source

@param [Sqreen::Attack] attack XXX: not used because we don't use Sqreen::Attack

# File lib/sqreen/signals/conversions.rb, line 45
def convert_attack(attack)
  # no need to set actor/context as we only include them in request records/traces
  location_h = {}
  location_h.merge!(stack_trace: attack.backtrace) if attack.backtrace
  location_h.merge!(datadog_trace_id: datadog_trace_id) if attack.datadog_trace_id
  location_h.merge!(datadog_span_id: datadog_span_id) if attack.datadog_span_id
  location = Kit::Signals::Location.new(location_h) unless location_h.empty?

  Kit::Signals::Specialized::Attack.new(
    signal_name: "sq.agent.attack.#{attack.attack_type}",
    source: "sqreen:rule:#{attack.rulespack_id}:#{attack.rule_name}",
    time: attack.time,
    location: location,
    payload: Kit::Signals::Specialized::Attack::Payload.new(
      test: attack.test?,
      block: attack.block?,
      infos: attack.infos
    )
  )
end
convert_batch(batch) click to toggle source

@return [Array<Sqreen::Kit::Signals::Signal|Sqreen::Kit::Signals::Trace>]

# File lib/sqreen/signals/conversions.rb, line 159
def convert_batch(batch)
  batch.map do |evt|
    case evt
    when RemoteException
      convert_exception(evt)
    when AggregatedMetric
      convert_metric_sample(evt)
    when RequestRecord
      convert_req_record(evt)
    when Sqreen::Kit::Signals::Signal
      evt
    when Sqreen::Kit::Signals::Trace
      evt
    else
      raise NotImplementedError, "Unknown type of event in batch: #{evt}"
    end
  end
end
convert_exception(exception) click to toggle source

@param [Sqreen::RemoteException] exception @return [Sqreen::Kit::Signals::Specialized::SqreenException]

# File lib/sqreen/signals/conversions.rb, line 89
def convert_exception(exception)
  payload = exception.payload

  infos = payload['client_ip'] ? { client_ip: payload['client_ip'] } : {}
  infos.merge!(payload['infos'] || {})

  Kit::Signals::Specialized::SqreenException.new(
    source: if payload['rule_name']
              "sqreen:rule:#{payload['rulespack_id']}:#{payload['rule_name']}"
            else
              agent_gen_source
            end,
    time: exception.time,
    ruby_exception: payload['exception'],
    infos: infos
  )
end
convert_metric_sample(agg) click to toggle source

@param [Sqreen::AggregatedMetric] agg @return [Sqreen::Kit::Signals::Metric]

# File lib/sqreen/signals/conversions.rb, line 25
def convert_metric_sample(agg)
  attrs = {
    signal_name: "sq.agent.metric.#{agg.name}",
    source: if agg.rule
              "sqreen:rules:#{agg.rule.rulespack_id}:#{agg.rule.rule_name}"
            else
              agent_gen_source
            end,
    time: agg.finish,
  }

  if agg.metric.is_a?(Sqreen::Metric::Binning)
    conv_binning_metric(agg, attrs)
  else
    conv_generic_metric(agg, attrs)
  end
end
convert_req_record(req_rec) click to toggle source

@param [Sqreen::RequestRecord] req_rec @return [Sqreen::Kit::Signals::Specialized::HttpTrace]

# File lib/sqreen/signals/conversions.rb, line 121
def convert_req_record(req_rec)
  payload = req_rec.payload

  request_p = payload['request']
  id_args = req_rec.last_identify_args
  identifiers = id_args[0] if id_args
  traits = id_args[1] if id_args

  observed = payload[:observed] || {}
  signals = []
  signals += (observed[:attacks] || [])
             .map { |att| convert_unstructured_attack(att) }
  signals += (observed[:sqreen_exceptions] || [])
             .map { |sq_exc| convert_unstructured_exception(sq_exc) }
  signals += req_rec.processed_sdk_calls
                    .select { |h| h[:name] == :track }
                    .map { |h| convert_track(h) }
  signals += (observed[:signals] || [])

  trace = Kit::Signals::Specialized::HttpTrace.new(
    actor: Kit::Signals::Actor.new(
      ip_addresses: [request_p[:client_ip]].compact,
      user_agent: request_p[:user_agent],
      identifiers: identifiers,
      traits: traits,
    ),
    location_infra: location_infra,
    context: convert_request(request_p,
                             payload['response'],
                             payload['headers'],
                             payload['params']),
    data: signals
  )
  HttpTraceRedaction.redact_trace!(trace, req_rec.redactor)
  trace
end
convert_unstructured_attack(payload) click to toggle source

see Sqreen::Rules::RuleCB.record_event

# File lib/sqreen/signals/conversions.rb, line 67
def convert_unstructured_attack(payload)
  location_h = {}
  location_h.merge!(stack_trace: payload[:backtrace]) if payload[:backtrace]
  location_h.merge!(datadog_trace_id: payload[:datadog_trace_id]) if payload[:datadog_span_id]
  location_h.merge!(datadog_span_id: payload[:datadog_span_id]) if payload[:datadog_span_id]
  location = Kit::Signals::Location.new(location_h) unless location_h.empty?

  Kit::Signals::Specialized::Attack.new(
    signal_name: "sq.agent.attack.#{payload[:attack_type]}",
    source: "sqreen:rule:#{payload[:rulespack_id]}:#{payload[:rule_name]}",
    time: payload[:time],
    location: location,
    payload: Kit::Signals::Specialized::Attack::Payload.new(
      test: payload[:test],
      block: payload[:block],
      infos: payload[:infos]
    )
  )
end
convert_unstructured_exception(payload) click to toggle source

see Sqreen::Rules::RuleCB.record_exception @param [Hash] payload @return [Sqreen::Kit::Signals::Specialized::SqreenException]

# File lib/sqreen/signals/conversions.rb, line 110
def convert_unstructured_exception(payload)
  Kit::Signals::Specialized::SqreenException.new(
    source: "sqreen:rule:#{payload[:rulespack_id]}:#{payload[:rule_name]}",
    time: payload[:time],
    ruby_exception: payload[:exception],
    infos: payload[:infos]
  )
end

Private Class Methods

agent_gen_source() click to toggle source
# File lib/sqreen/signals/conversions.rb, line 180
def agent_gen_source
  "sqreen:agent:ruby:#{Sqreen::VERSION}"
end
conv_binning_metric(agg, attrs) click to toggle source

@param [Sqreen::AggregatedMetric] agg @param [Hash] attrs

# File lib/sqreen/signals/conversions.rb, line 287
def conv_binning_metric(agg, attrs)
  attrs[:payload] = Kit::Signals::Specialized::BinningMetric::Payload.new(
    capture_interval_s: agg.metric.period,
    date_started: agg.start,
    date_ended: agg.finish,
    base: agg.data['b'],
    unit: agg.data['u'],
    max: agg.data['v']['max'],
    bins: agg.data['v'].reject { |k, _v| k == 'max' }
  )

  Kit::Signals::Specialized::BinningMetric.new(attrs)
end
conv_generic_metric(agg, attrs) click to toggle source

@param [Sqreen::AggregatedMetric] agg @param [Hash] attrs

# File lib/sqreen/signals/conversions.rb, line 273
def conv_generic_metric(agg, attrs)
  attrs[:payload] = Kit::Signals::Specialized::AggregatedMetric::Payload.new(
    kind: metric_kind(agg.metric),
    capture_interval_s: agg.metric.period,
    date_started: agg.start,
    date_ended: agg.finish,
    values: agg.data
  )

  Kit::Signals::Specialized::AggregatedMetric.new(attrs)
end
convert_request(req_payload, resp_payload, headers_payload, params_payload) click to toggle source

@param [Hash] req_payload @param [Hash] headers_payload @param [Hash] params_payload see the PayloadCreator abomination for reference TODO: do not convert from the old payload to the new payload Have an intermediate object that gets the data from the framework. (Or convert directly from the framework, but this needs to be done during the request, not just before event is transmitted)

# File lib/sqreen/signals/conversions.rb, line 218
def convert_request(req_payload, resp_payload, headers_payload, params_payload)
  req_payload ||= {}
  headers_payload ||= {}
  resp_payload ||= {}
  params_payload ||= {}

  other = params_payload['other']
  other = merge_hash_append(other, params_payload['rack'])
  other = merge_hash_append(other, params_payload['grape_params'])
  other = merge_hash_append(other, params_payload['rack_routing'])

  Sqreen::Kit::Signals::Context::HttpContext.new(
    {
      rid: req_payload[:rid],
      headers: headers_payload,
      user_agent: req_payload[:user_agent],
      scheme: req_payload[:scheme],
      verb: req_payload[:verb],
      host: req_payload[:host],
      port: req_payload[:port],
      remote_ip: req_payload[:remote_ip],
      remote_port: req_payload[:remote_port] || 0,
      path: req_payload[:path],
      referer: req_payload[:referer],
      params_query: params_payload['query'],
      params_form: params_payload['form'],
      params_other: other,
      # endpoint, is_reveal_replayed not set
      status: resp_payload[:status],
      content_length: resp_payload[:content_length],
      content_type: resp_payload[:content_type],
      # datadog
      datadog_trace_id: req_payload[:datadog_trace_id],
      datadog_span_id: req_payload[:datadog_span_id],
    }
  )
end
convert_track(call_info) click to toggle source

see Sqreen::RequestRecord.processed_sdk_calls

# File lib/sqreen/signals/conversions.rb, line 198
def convert_track(call_info)
  options = call_info[:args][1] || {}
  Kit::Signals::Specialized::SdkTrackCall.new(
    signal_name: "sq.sdk.#{call_info[:args][0]}",
    time: call_info[:time],
    payload: Kit::Signals::Specialized::SdkTrackCall::Payload.new(
      properties: options[:properties],
      user_identifiers: options[:user_identifiers]
    )
  )
end
location_infra() click to toggle source
# File lib/sqreen/signals/conversions.rb, line 184
def location_infra
  @location_infra ||= begin
    Kit::Signals::LocationInfra.new(
      agent_version: Sqreen::VERSION,
      os_type: RuntimeInfos.os[:os_type],
      hostname: RuntimeInfos.hostname,
      runtime_type: RuntimeInfos.runtime[:runtime_type],
      runtime_version: RuntimeInfos.runtime[:runtime_version],
      libsqreen_version: RuntimeInfos.libsqreen_version,
    )
  end
end
merge_hash_append(hash1, hash2) click to toggle source
# File lib/sqreen/signals/conversions.rb, line 256
def merge_hash_append(hash1, hash2)
  return nil if hash1.nil? && hash2.nil?
  return hash1 if hash2.nil? || hash2.empty?
  return hash2 if hash1.nil? || hash1.empty?

  pairs = (hash1.keys + hash2.keys).map do |key|
    values1 = hash1[key]
    values2 = hash2[key]
    values = [values1, values2].compact
    values = values.first if values.size == 1
    [key, values]
  end
  Hash[pairs]
end
metric_kind(metric) click to toggle source

@param [Sqreen::Metric::Base] metric

# File lib/sqreen/signals/conversions.rb, line 302
def metric_kind(metric)
  metric.class.name.sub(/.*::/, '').sub(/Metric$/, '')
end