lib/google/ads/google_ads/interceptors/logging_interceptor.rb in google-ads-googleads-7.0.0 vs lib/google/ads/google_ads/interceptors/logging_interceptor.rb in google-ads-googleads-8.0.0
- old
+ new
@@ -25,10 +25,21 @@
module Ads
module GoogleAds
module Interceptors
class LoggingInterceptor < GRPC::ClientInterceptor
+ HEADERS_TO_MASK = [:"developer-token"]
+ SEARCH_RESPONSE_FIELDS_TO_MASK = %w[
+ emailAddress
+ inviterUserEmailAddress
+ userEmail
+ ]
+ SEARCH_REQUEST_MASK =
+ /customer_user_access.email_address|change_event.user_email/
+
+ MASK_REPLACEMENT = "REDACTED"
+
def initialize(logger)
# Don't propagate args, parens are necessary
super()
@logger = logger
end
@@ -134,23 +145,92 @@
exception_details
end
def build_success_response_message(response)
- "Incoming response: Payload: #{response.to_json}"
+ "Incoming response: Payload: #{sanitize_message(response).to_json}"
end
def build_request_message(metadata, request)
# calling #to_json on some protos (specifically those with non-UTF8
# encodable byte values) causes a segfault, however #inspect works
# so we check if the proto contains a bytevalue, and if it does
# we #inspect instead of #to_json
request_inspect = if use_bytes_inspect?(request)
request.inspect
else
- request.to_json
+ sanitize_message(request).to_json
end
- "Outgoing request: Headers: #{metadata.to_json} Payload: #{request_inspect}"
+ "Outgoing request: Headers: #{sanitize_headers(metadata).to_json} " \
+ "Payload: #{request_inspect}"
+ end
+
+ def sanitize_headers(metadata)
+ metadata = metadata.clone
+ HEADERS_TO_MASK.each do |header|
+ metadata[header] = MASK_REPLACEMENT
+ end
+ metadata
+ end
+
+ def sanitize_message(message)
+ message_class = message.class.to_s.split("::").last
+ if %w[SearchGoogleAdsStreamResponse SearchGoogleAdsResponse].include?(
+ message_class)
+ # Sanitize all known sensitive fields across all search responses.
+ message = JSON.parse(message.to_json)
+ message["fieldMask"].split(",").each do |path|
+ if SEARCH_RESPONSE_FIELDS_TO_MASK.include?(path.split(".").last)
+ message["results"].each do |result|
+ sanitize_field(result, path)
+ end
+ end
+ end
+ message
+ elsif %w[SearchGoogleAdsRequest SearchGoogleAdsStreamRequest].include?(
+ message_class)
+ if SEARCH_REQUEST_MASK === message.query
+ message = JSON.parse(message.to_json)
+ message["query"] = MASK_REPLACEMENT
+ end
+ message
+ elsif "CustomerUserAccess" == message_class
+ # Sanitize sensitive fields specific to CustomerUserAccess get requests.
+ message = JSON.parse(message.to_json)
+ sanitize_customer_user_access(message)
+ elsif "MutateCustomerUserAccessRequest" == message_class
+ # Sanitize sensitive fields when mutating a CustomerUserAccess.
+ message = JSON.parse(message.to_json)
+ if message.include?("operation") && message["operation"].include?("update")
+ message["operation"]["update"] =
+ sanitize_customer_user_access(message["operation"]["update"])
+ end
+ message
+ else
+ message
+ end
+ end
+
+ def sanitize_customer_user_access(message)
+ if message.include?("emailAddress")
+ message["emailAddress"] = MASK_REPLACEMENT
+ end
+ if message.include?("inviterUserEmailAddress")
+ message["inviterUserEmailAddress"] = MASK_REPLACEMENT
+ end
+ message
+ end
+
+ def sanitize_field(object, path)
+ split_path = path.split(".")
+ target_field = split_path.last
+ split_path.inject(object) do |obj, field|
+ if obj.include?(target_field)
+ obj[target_field] = MASK_REPLACEMENT
+ break
+ end
+ obj[field]
+ end
end
def build_summary_message(request, call, method, is_fault)
customer_id = "N/A"
customer_id = request.customer_id if request.respond_to?(:customer_id)