# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Agent module Reporting # This is the new Finding class which will include all the needed information for the new reporting system to # relay this information in the Finding/Trace messages. These findings are used by TeamServer to construct the # vulnerability information for the assess feature. They represent those parts of the application, either through # configuration, method invocation, or dataflow, which are determined to be insecure. # # @attr_accessor events [Array] if a dataflow based finding, the # representation of those method calls which constitute a dangerous code path. # @attr_accessor properties [Hash] a set of values that TeamServer can use to provide more context # to the user when rendering the finding. For some findings, a specific set of keys and values are required. # @attr_accessor request [Contrast::Agent::Request, nil] the request, if any, in which this finding occurred # @attr_accessor hash_code [String] the unique identifier of this finding. # @attr_reader rule_id [String] the name of the rule violated; must match those in TeamServer. class FindingRequest attr_reader :body, :headers, :method, :parameters, :port, :protocol, :query_string, :uri, :version class << self # @param request [Contrast::Api::Dtm::HttpRequest,Contrast::Agent::Request] # @return [Contrast::Agent::Reporting::FindingRequest] def convert request report = new if request.cs__is_a?(Contrast::Agent::Request) report.attach_data(request) else report.attach_dtm_data(request) end report end end # Parse the data from a Contrast::Agent::Request to attach what is required for reporting to TeamServer to this # Contrast::Agent::Reporting::FindingRequest # # @param request [Contrast::Agent::Request] def attach_data request @body = request.body @headers = {} request.headers.each_pair do |key, value| next unless key && value # We need to change from the uppercase _ format to capitalized - format. header = key.split('_') header.each(&:capitalize!) header = header.join('-') headers[header] = value.split end @method = request.request_method @parameters = {} request.parameters.each_pair do |key, value| @parameters[key] = value end @port = request.port || 0 @protocol = request.scheme @query_string = request.query_string @uri = request.normalized_uri @version = request.version end # Parse the data from a Contrast::Api::Dtm::HttpRequest to attach what is required for reporting to TeamServer # to this Contrast::Agent::Reporting::FindingRequest # # @param request_dtm [Contrast::Api::Dtm::HttpRequest] def attach_dtm_data request_dtm @body = request_dtm.request_body_binary @headers = {} request_dtm.request_headers.each_pair do |key, value| next unless key && value # We need to change from the uppercase _ format to capitalized - format. header = key.split('_') header.each(&:capitalize!) header = header.join('-') headers[header] = value.split end @method = request_dtm.method # rubocop:disable Security/Object/Method @parameters = {} request_dtm.normalized_request_params.each_value do |pair| @parameters[pair.key] = pair.values end @port = nil @protocol = request_dtm.protocol @query_string = request_dtm.query_string @uri = request_dtm.uri @version = request_dtm.version 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 { body: body, headers: headers, method: method, # rubocop:disable Security/Object/Method parameters: parameters, port: port || 0, protocol: protocol, queryString: query_string, uri: uri, version: version } end def validate unless cs__method && !cs__method.empty? raise(ArgumentError, "#{ self } did not have a proper method. Unable to continue.") end raise(ArgumentError, "#{ self } did not have a proper uri. Unable to continue.") unless uri && !uri.empty? end end end end end