# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Utils # used in Contrast::Agent::Response module ResponseUtils private # From the dtm for normalized_response_headers: # Key is UPPERCASE_UNDERSCORE # # Example: Content-Type: text/html; charset=utf-8 # "CONTENT_TYPE" => Content-Type,["text/html; charset=utf8"] def append_pair map, key, value return unless key && value return if value.is_a?(Hash) safe_key = Contrast::Utils::StringUtils.force_utf8(key) hash_key = Contrast::Utils::StringUtils.normalized_key(safe_key) map[hash_key] ||= Contrast::Api::Dtm::Pair.new map[hash_key].key = safe_key map[hash_key].values << Contrast::Utils::StringUtils.force_utf8(value) end HTTP_PREFIX = /^[Hh][Tt][Tt][Pp][_-]/i.cs__freeze # Given some holder of the content of the response's body, extract that # content and return it as a String # # @param body [String, Rack::File, Rack::BodyProxy, # ActionDispatch::Response::RackBody, Rack::Response] Something that # holds, wraps, or is the body of the Response # @return [nil, String] the content of the body def extract_body body return unless body return if defined?(Rack::File) && body.is_a?(Rack::File) return handle_rack_body_proxy(body) if body.is_a?(Rack::BodyProxy) return extract_body(body.body) if sub_extractable?(body) return enumerable_text_from(body) if Contrast::Utils::DuckUtils.quacks_to?(body, :each) # https://stackoverflow.com/questions/15654676/how-to-convert-activesupportsafebuffer-to-string return body.to_str if body.is_a?(ActionView::OutputBuffer) read_or_string(body) end def sub_extractable? body (defined?(ActionDispatch::Response::RackBody) && body.is_a?(ActionDispatch::Response::RackBody)) || body.is_a?(Rack::Response) end def enumerable_text_from body entries = body.map { |entry| read_or_string(entry) } entries.compact! entries.join(Contrast::Utils::ObjectShare::NEW_LINE) end def handle_rack_body_proxy body next_body = body.instance_variable_get(:@body) case next_body when Array extract_body(next_body[0]) else extract_body(next_body) end end def read_or_string obj return unless obj if Contrast::Utils::DuckUtils.quacks_to?(obj, :read) tmp = obj.read obj.rewind tmp else obj.to_s end end end end end