# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/utils/string_utils' module Contrast module Api module Decorators # Used to decorate the {Contrast::Api::Dtm::TraceEvent} protobuf model to # convert our {Contrast::Agent::AssessContrastEvent} to the dtm. module TraceEvent def self.included klass klass.extend(ClassMethods) end # The TeamServer uses the Event's type and action to render it in the Details page. These values control the # left-hand "What happened" column and the data shown in the right-hand data # # @param contrast_event [Contrast::Agent::Assess::ContrastEvent] # @return [Contrast::Api::Dtm::TraceEvent] def build_display_params! contrast_event self.type = contrast_event.policy_node.node_type self.action = contrast_event.policy_node.build_action self end # The TeamServer uses the Event's representation of the data to render the actual data used in the dataflow on # the Details page. # # @param contrast_event [Contrast::Agent::Assess::ContrastEvent] # @return [Contrast::Api::Dtm::TraceEvent] def build_dataflow! contrast_event # Figure out what the target of this event was. This can't be pulled into the decorator because SourceEvent # has a custom impl :/ taint_target = contrast_event.determine_taint_target(self) truncate_obj = Contrast::Utils::ObjectShare::OBJECT_KEY != taint_target self.object = Contrast::Api::Dtm::TraceEventObject.build(contrast_event.object, truncate_obj) truncate_ret = Contrast::Utils::ObjectShare::RETURN_KEY != taint_target self.ret = Contrast::Api::Dtm::TraceEventObject.build(contrast_event.ret, truncate_ret) build_event_args!(contrast_event, taint_target) build_taint_ranges!(contrast_event) self end # Wrapper around build_event_object for the args array. Handles # tainting the correct argument. # @return [Contrast::Api::Dtm::TraceEvent] def build_event_args! contrast_event, taint_target contrast_event.args.each_index do |idx| truncate_arg = taint_target != idx event_arg = Contrast::Api::Dtm::TraceEventObject.build(contrast_event.args[idx], truncate_arg) args << event_arg end self end # TeamServer only supports one object's taint ranges at a time. We keep # those tags on the event directly, so we just need to convert them to # their DTM form in order to report this. # # @param contrast_event [Contrast::Agent::AssessContrastEvent] # @return [Contrast::Api::Dtm::TraceEvent] def build_taint_ranges! contrast_event # If there's no taint_target, this isn't a dataflow trace, but a # trigger one return self unless contrast_event&.tags self.taint_ranges += Contrast::Api::Dtm::TraceTaintRange.build_for_event(contrast_event.tags) self end # For each Parent in the ContrastEvent, capture its id and report it to TeamServer. # # @param contrast_event [Contrast::Agent::AssessContrastEvent] # @return [Contrast::Api::Dtm::TraceEvent] def build_parent_ids! contrast_event contrast_event&.parent_events&.each do |event| next unless event parent = Contrast::Api::Dtm::ParentObjectId.new parent.id = event.event_id.to_i parent_object_ids << parent end self end # Convert the caller into the Stack DTM TeamServer consumes # # @param contrast_event [Contrast::Agent::AssessContrastEvent] # @return [Contrast::Api::Dtm::TraceEvent] def build_stack! contrast_event # We delayed doing this as long as possible b/c it's expensive stack_dtms = Contrast::Utils::StackTraceUtils.build_assess_stack_array(contrast_event.stack_trace) self.stack += stack_dtms self end # Class methods for TraceEvent module ClassMethods def build contrast_event event_dtm = new event_dtm.build_display_params!(contrast_event) event_dtm.build_dataflow!(contrast_event) event_dtm.build_stack!(contrast_event) event_dtm.timestamp_ms = contrast_event.time.to_i event_dtm.thread = Contrast::Utils::StringUtils.force_utf8(contrast_event.thread) event_dtm.build_parent_ids!(contrast_event) event_dtm.object_id = contrast_event.event_id.to_i event_dtm.signature = Contrast::Api::Dtm::TraceEventSignature.build(contrast_event.ret, contrast_event.policy_node, contrast_event.args) event_dtm end end end end end end Contrast::Api::Dtm::TraceEvent.include(Contrast::Api::Decorators::TraceEvent)