# typed: true # Copyright (c) 2015 Sqreen. All Rights Reserved. # Please refer to our terms for more information: https://www.sqreen.com/terms.html # TODO: Sqreen::Attack => sqreen/events # TODO: Sqreen::RemoteException => sqreen/events # TODO: Sqreen::RequestRecord => sqreen/events # TODO: Sqreen.time require 'sqreen/events/attack' require 'sqreen/events/remote_exception' require 'sqreen/mono_time' require 'sqreen/deliveries/simple' module Sqreen module Deliveries # Simple delivery method that batch event already seen in a batch class Batch < Simple attr_accessor :max_batch, :max_staleness attr_accessor :current_batch, :first_seen def initialize(session, max_batch, max_staleness, randomize_staleness = true) super(session) self.max_batch = max_batch self.max_staleness = max_staleness @original_max_staleness = max_staleness self.current_batch = [] @first_seen = {} @randomize_staleness = randomize_staleness end def post_event(event) current_batch.push(event) post_batch if post_batch_needed?(event) end def drain post_batch unless current_batch.empty? end def tick post_batch if !current_batch.empty? && stale? end protected def stale? min = @first_seen.values.min return false if min.nil? (min + max_staleness) < Sqreen.time end def post_batch_needed?(event) now = Sqreen.time # do not use any? {} due to side effects inside block event_keys(event).map do |key| was = @first_seen[key] @first_seen[key] ||= now was.nil? || current_batch.size > max_batch || now > (was + max_staleness) end.any? end def post_batch session.post_batch(current_batch) current_batch.clear now = Sqreen.time @first_seen.each_key do |key| @first_seen[key] = now end return unless @randomize_staleness self.max_staleness = @original_max_staleness # Adds up to 10% of lateness self.max_staleness += rand(@original_max_staleness / 10) end def event_keys(event) return [event_key(event)] unless event.is_a?(Sqreen::RequestRecord) res = [] res += event.observed.fetch(:attacks, []).map { |e| "att-#{e[:rule_name]}" } res += event.observed.fetch(:sqreen_exceptions, []).map { |e| "rex-#{e[:exception].class}" } res += event.observed.fetch(:sdk, []).select { |e| e[0] == :track }.map { |e| "sdk-track".freeze } return res end def event_key(event) case event when Sqreen::Attack "att-#{event.type}" when Sqreen::RemoteException "rex-#{event.klass}" end end end end end