11# frozen_string_literal: true
22
3+ require "json"
4+
35module Sentry
46 class RequestInterface < Interface
57 REQUEST_ID_HEADERS = %w[ action_dispatch.request_id HTTP_X_REQUEST_ID ] . freeze
68 CONTENT_HEADERS = %w[ CONTENT_TYPE CONTENT_LENGTH ] . freeze
7- IP_HEADERS = [
8- "REMOTE_ADDR" ,
9- "HTTP_CLIENT_IP" ,
10- "HTTP_X_REAL_IP" ,
11- "HTTP_X_FORWARDED_FOR"
12- ] . freeze
139
1410 # Regex to detect lowercase chars — match? is allocation-free (no MatchData/String)
1511 LOWERCASE_PATTERN = /[a-z]/ . freeze
@@ -27,10 +23,10 @@ class RequestInterface < Interface
2723 # @return [Hash]
2824 attr_accessor :data
2925
30- # @return [String]
26+ # @return [String, Hash ]
3127 attr_accessor :query_string
3228
33- # @return [String ]
29+ # @return [Hash ]
3430 attr_accessor :cookies
3531
3632 # @return [Hash]
@@ -40,33 +36,24 @@ class RequestInterface < Interface
4036 attr_accessor :env
4137
4238 # @param env [Hash]
43- # @param send_default_pii [Boolean]
39+ # @param data_collection [DataCollection]
40+ # @param send_default_pii [Boolean] Deprecated compatibility input, unused.
4441 # @param rack_env_whitelist [Array]
42+ # @see Configuration#data_collection
4543 # @see Configuration#send_default_pii
4644 # @see Configuration#rack_env_whitelist
47- def initialize ( env :, send_default_pii :, rack_env_whitelist :)
45+ def initialize ( env :, data_collection :, rack_env_whitelist :, send_default_pii : nil )
4846 env = env . dup
49-
50- unless send_default_pii
51- # need to completely wipe out ip addresses
52- RequestInterface ::IP_HEADERS . each do |header |
53- env . delete ( header )
54- end
55- end
56-
5747 request = ::Rack ::Request . new ( env )
58-
59- if send_default_pii
60- self . data = read_data_from ( request )
61- self . cookies = request . cookies
62- self . query_string = request . query_string
63- end
64-
65- self . url = request . scheme && request . url . split ( "?" ) . first
66- self . method = request . request_method
67-
68- self . headers = filter_and_format_headers ( env , send_default_pii )
69- self . env = filter_and_format_env ( env , rack_env_whitelist )
48+ query = data_collection . url_query_params . filter ( request . GET ) rescue nil
49+
50+ self . method = request . request_method
51+ self . url = request . scheme && request . url . split ( "?" ) . first
52+ self . query_string = query unless query &.empty?
53+ self . cookies = data_collection . cookies . filter ( request . cookies , cookie : true )
54+ self . data = read_data_from ( request ) if data_collection . http_bodies . include? ( :incoming_request )
55+ self . headers = filter_and_format_headers ( env , data_collection . http_headers . request )
56+ self . env = filter_and_format_env ( env , data_collection . http_headers . request , rack_env_whitelist )
7057 end
7158
7259 private
@@ -75,25 +62,31 @@ def read_data_from(request)
7562 return "Skipped non-rewindable request body" unless request . body . respond_to? ( :rewind )
7663
7764 if request . form_data?
78- request . POST
79- elsif request . body # JSON requests, etc
80- data = request . body . read ( MAX_BODY_LIMIT )
81- data = Utils ::EncodingHelper . encode_to_utf_8 ( data . to_s )
82- request . body . rewind
83- data
65+ DataCollection . filter ( request . POST )
66+ else
67+ body = request . body . read ( MAX_BODY_LIMIT )
68+ body = Utils ::EncodingHelper . encode_to_utf_8 ( body . to_s )
69+
70+ if request . media_type == "application/json" || request . media_type &.end_with? ( "+json" )
71+ parsed_body = JSON . parse ( body )
72+ parsed_body . is_a? ( Hash ) ? DataCollection . filter ( parsed_body ) : parsed_body
73+ else
74+ body
75+ end
8476 end
85- rescue IOError => e
77+ rescue JSON :: ParserError , IOError => e
8678 e . message
79+ ensure
80+ request . body . rewind if request . body . respond_to? ( :rewind )
8781 end
8882
89- def filter_and_format_headers ( env , send_default_pii )
83+ def filter_and_format_headers ( env , collection )
9084 env . each_with_object ( { } ) do |( key , value ) , memo |
9185 begin
9286 key = key . to_s # rack env can contain symbols
9387 next memo [ "X-Request-Id" ] ||= Utils ::RequestId . read_from ( env ) if Utils ::RequestId ::REQUEST_ID_HEADERS . include? ( key )
9488 next if is_server_protocol? ( key , value , env [ "SERVER_PROTOCOL" ] )
9589 next if is_skippable_header? ( key )
96- next if key == "HTTP_AUTHORIZATION" && !send_default_pii
9790
9891 # Rack stores headers as HTTP_WHAT_EVER, we need What-Ever
9992 key = key . delete_prefix ( "HTTP_" )
@@ -107,12 +100,13 @@ def filter_and_format_headers(env, send_default_pii)
107100 Sentry . sdk_logger . warn ( LOGGER_PROGNAME ) { "Error raised while formatting headers: #{ e . message } " }
108101 next
109102 end
103+ end . then do |e |
104+ collection . filter ( e )
110105 end
111106 end
112107
113108 def is_skippable_header? ( key )
114109 key . match? ( LOWERCASE_PATTERN ) || # lower-case envs aren't real http headers
115- key == "HTTP_COOKIE" || # Cookies don't go here, they go somewhere else
116110 !( key . start_with? ( "HTTP_" ) || CONTENT_HEADERS . include? ( key ) )
117111 end
118112
@@ -134,11 +128,15 @@ def self.rack_3_or_above?
134128 Gem ::Version . new ( ::Rack . release ) >= Gem ::Version . new ( "3.0" )
135129 end
136130
137- def filter_and_format_env ( env , rack_env_whitelist )
138- return env if rack_env_whitelist . empty?
139-
140- env . select do |k , _v |
141- rack_env_whitelist . include? k . to_s
131+ def filter_and_format_env ( env , collection , rack_env_whitelist )
132+ if rack_env_whitelist . empty?
133+ env
134+ else
135+ env . select do |k , _v |
136+ rack_env_whitelist . include? k . to_s
137+ end
138+ end . then do |e |
139+ collection . filter ( e )
142140 end
143141 end
144142 end
0 commit comments