# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'base64' require 'contrast/agent/assess/contrast_object' module Contrast module Agent module Reporting # This is the new FindingEventObject class which will include all the needed information for the new reporting # system to relay this information in the Finding/Trace messages. These FindingEventObjects are used by # TeamServer to construct the vulnerability information for the assess feature. They represent those parts of the # objects that were acted on in a Dataflow Finding. class FindingEventObject # @return [Integer] the id of the Object this represents. attr_reader :hash # @return [Boolean] if the Object is tracked or not attr_reader :tracked # @return [String] the base64 of the human readable representation of the Object this represents. attr_reader :value # We'll truncate any object that isn't important to the taint ranges of this event, so that we don't murder # TeamServer by, for instance, hypothetically sending the entire rendered HTML page >_> <_< >_> ELLIPSIS = '...' UNTRUNCATED_PORTION_LENGTH = 25 TRUNCATION_LENGTH = (UNTRUNCATED_PORTION_LENGTH * 2) + ELLIPSIS.length class << self # @param object [Contrast::Agent::Assess::ContrastObject] the object to translate # @param truncate [Boolean] if the value of this FindingEventObject should be truncated or not # @return [Contrast::Agent::Reporting::FindingEventObject] def convert object, truncate report = new report.attach_data(object, truncate) report end end # Parse the data from a Contrast::Agent::Assess::ContrastObject to attach what is required for reporting to # TeamServer to this Contrast::Agent::Reporting::FindingEventObject # # @param object [Contrast::Agent::Assess::ContrastObject, nil] def attach_data object, truncate @hash = object ? object.tracked_object_id : nil.__id__ @tracked = !!object&.tracked? @value = reportable_value(object&.object, truncate) end # Convert the instance variables on the class, and other information, into the identifiers required for # TeamServer to process the JSON form of this message. # # @return [Hash] # @raise [ArgumentError] def to_controlled_hash validate { hash: hash, tracked: tracked, value: value } end def validate raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash raise(ArgumentError, "#{ self } did not have a proper tracked. Unable to continue.") if tracked.nil? return if value raise(ArgumentError, "#{ self } did not have a proper value. Unable to continue.") end private # Parse, truncate, and translate the given value to be reported to TeamServer. The field is expected to be # base64 encoded. # # @param value [String, nil] the contrast_string of the object this represents. # @param truncate [Boolean] if the string should be truncated or not. # @return [String] The strict_encode64 value of the String sent to TeamServer to represent this object. def reportable_value value, truncate return Contrast::Utils::ObjectShare::NIL_64_STRING unless value if truncate && value.length > TRUNCATION_LENGTH tmp = [] tmp << value[0, UNTRUNCATED_PORTION_LENGTH] tmp << ELLIPSIS tmp << value[value.length - UNTRUNCATED_PORTION_LENGTH, UNTRUNCATED_PORTION_LENGTH] value = tmp.join end Base64.strict_encode64(value) end end end end end