Skip to content

Commit

Permalink
Don't gate request query strings and form data behind send_default_pii
Browse files Browse the repository at this point in the history
  • Loading branch information
sl0thentr0py committed Oct 31, 2024
1 parent 913c75e commit c6d6137
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 181 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Remove `config.async` [#1894](https://github.com/getsentry/sentry-ruby/pull/1894)
- Migrate from to_hash to to_h ([#2351](https://github.com/getsentry/sentry-ruby/pull/2351))
- Query strings and form data in requests are no longer controlled by `send_default_pii` ([#2452](https://github.com/getsentry/sentry-ruby/pull/2452))

## Unreleased

Expand Down
15 changes: 6 additions & 9 deletions sentry-ruby/lib/sentry/faraday.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,12 @@ def instrument(op_name, env, &block)
private

def extract_request_info(env)
url = env[:url].scheme + "://" + env[:url].host + env[:url].path
result = { method: env[:method].to_s.upcase, url: url }

if Sentry.configuration.send_default_pii
result[:query] = env[:url].query
result[:body] = env[:body]
end

result
{
method: env[:method].to_s.upcase,
url: env[:url].scheme + "://" + env[:url].host + env[:url].path,
query: env[:url].query,
body: env[:body]
}
end
end
end
Expand Down
10 changes: 4 additions & 6 deletions sentry-ruby/lib/sentry/interfaces/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,12 @@ def initialize(env:, send_default_pii:, rack_env_whitelist:)

request = ::Rack::Request.new(env)

if send_default_pii
self.data = read_data_from(request)
self.cookies = request.cookies
self.query_string = request.query_string
end

self.url = request.scheme && request.url.split("?").first
self.method = request.request_method
self.data = read_data_from(request)
self.query_string = request.query_string

self.cookies = request.cookies if send_default_pii

self.headers = filter_and_format_headers(env, send_default_pii)
self.env = filter_and_format_env(env, rack_env_whitelist)
Expand Down
14 changes: 6 additions & 8 deletions sentry-ruby/lib/sentry/net/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,12 @@ def extract_request_info(req)
uri = req.uri || URI.parse(URI::DEFAULT_PARSER.escape("#{use_ssl? ? 'https' : 'http'}://#{hostname}#{req.path}"))
url = "#{uri.scheme}://#{uri.host}#{uri.path}" rescue uri.to_s

result = { method: req.method, url: url }

if Sentry.configuration.send_default_pii
result[:query] = uri.query
result[:body] = req.body
end

result
{
method: req.method,
url: url,
query: uri.query,
body: req.body
}
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion sentry-ruby/lib/sentry/utils/http_tracing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def record_sentry_breadcrumb(request_info, response_status)
level: :info,
category: self.class::BREADCRUMB_CATEGORY,
type: :info,
data: { status: response_status, **request_info }
data: { status: response_status, **request_info.compact }
)

Sentry.add_breadcrumb(crumb)
Expand Down
101 changes: 30 additions & 71 deletions sentry-ruby/spec/sentry/breadcrumb/http_logger_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,76 +21,35 @@
end
end

context "with config.send_default_pii = true" do
before do
Sentry.configuration.send_default_pii = true
end

it "adds http breadcrumbs with query string & request body" do
stub_normal_response

response = Net::HTTP.get_response(URI("http://example.com/path?foo=bar"))

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "GET", url: "http://example.com/path", query: "foo=bar", body: nil })

http = Net::HTTP.new("example.com")
request = Net::HTTP::Get.new("/path?foo=bar")
response = http.request(request)

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "GET", url: "http://example.com/path", query: "foo=bar", body: nil })

request = Net::HTTP::Post.new("/path?foo=bar")
request.body = 'quz=qux'
response = http.request(request)

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq(
{ status: 200, method: "POST", url: "http://example.com/path", query: "foo=bar", body: 'quz=qux' }
)
end
end

context "with config.send_default_pii = false" do
before do
Sentry.configuration.send_default_pii = false
end

it "adds http breadcrumbs without query string & request body" do
stub_normal_response

response = Net::HTTP.get_response(URI("http://example.com/path?foo=bar"))

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "GET", url: "http://example.com/path" })

http = Net::HTTP.new("example.com")
request = Net::HTTP::Get.new("/path?foo=bar")
response = http.request(request)

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "GET", url: "http://example.com/path" })

request = Net::HTTP::Post.new("/path?foo=bar")
request.body = 'quz=qux'
response = http.request(request)

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "POST", url: "http://example.com/path" })
end
it "adds http breadcrumbs with query string & request body" do
stub_normal_response

response = Net::HTTP.get_response(URI("http://example.com/path?foo=bar"))

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "GET", url: "http://example.com/path", query: "foo=bar" })

http = Net::HTTP.new("example.com")
request = Net::HTTP::Get.new("/path?foo=bar")
response = http.request(request)

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "GET", url: "http://example.com/path", query: "foo=bar" })

request = Net::HTTP::Post.new("/path?foo=bar")
request.body = 'quz=qux'
response = http.request(request)

expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq(
{ status: 200, method: "POST", url: "http://example.com/path", query: "foo=bar", body: 'quz=qux' }
)
end

it "doesn't record breadcrumb for the SDK's request" do
Expand All @@ -115,7 +74,7 @@
expect(response.code).to eq("200")
crumb = Sentry.get_current_scope.breadcrumbs.peek
expect(crumb.category).to eq("net.http")
expect(crumb.data).to eq({ status: 200, method: "GET", url: "http://example.com/path" })
expect(crumb.data).to eq({ status: 200, method: "GET", query: "foo=bar", url: "http://example.com/path" })
end
end
end
2 changes: 2 additions & 0 deletions sentry-ruby/spec/sentry/event_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
headers: { 'Host' => 'localhost', 'X-Request-Id' => 'abcd-1234-abcd-1234' },
method: 'POST',
url: 'http://localhost/lol',
query_string: 'biz=baz',
data: { 'foo' => 'bar' }
)
expect(event.to_h[:tags][:request_id]).to eq("abcd-1234-abcd-1234")
expect(event.to_h[:user][:ip_address]).to eq(nil)
Expand Down
36 changes: 9 additions & 27 deletions sentry-ruby/spec/sentry/interfaces/request_interface_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,22 +139,22 @@ def to_s
end
end

it "doesn't store request body by default" do
env.merge!("REQUEST_METHOD" => "POST", ::Rack::RACK_INPUT => StringIO.new("data=ignore me"))
it "stores form data" do
env.merge!("REQUEST_METHOD" => "POST", ::Rack::RACK_INPUT => StringIO.new("data=catch me"))

expect(subject.data).to eq(nil)
expect(subject.data).to eq({ "data" => "catch me" })
end

it "doesn't store request body by default" do
env.merge!(::Rack::RACK_INPUT => StringIO.new("ignore me"))
it "stores query string" do
env.merge!("QUERY_STRING" => "token=xxxx")

expect(subject.data).to eq(nil)
expect(subject.query_string).to eq("token=xxxx")
end

it "doesn't store query_string by default" do
env.merge!("QUERY_STRING" => "token=xxxx")
it "stores request body" do
env.merge!(::Rack::RACK_INPUT => StringIO.new("catch me"))

expect(subject.query_string).to eq(nil)
expect(subject.data).to eq("catch me")
end

context "with config.send_default_pii = true" do
Expand All @@ -166,24 +166,6 @@ def to_s
expect(subject.cookies).to eq("cookies!")
end

it "stores form data" do
env.merge!("REQUEST_METHOD" => "POST", ::Rack::RACK_INPUT => StringIO.new("data=catch me"))

expect(subject.data).to eq({ "data" => "catch me" })
end

it "stores query string" do
env.merge!("QUERY_STRING" => "token=xxxx")

expect(subject.query_string).to eq("token=xxxx")
end

it "stores request body" do
env.merge!(::Rack::RACK_INPUT => StringIO.new("catch me"))

expect(subject.data).to eq("catch me")
end

it "stores Authorization header" do
env.merge!("HTTP_AUTHORIZATION" => "Basic YWxhZGRpbjpvcGVuc2VzYW1l")

Expand Down
83 changes: 24 additions & 59 deletions sentry-ruby/spec/sentry/net/http_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,67 +46,30 @@
end
end

context "with config.send_default_pii = true" do
before do
Sentry.configuration.send_default_pii = true
end

it "records the request's span with query string in data" do
stub_normal_response

transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)

response = Net::HTTP.get_response(URI("http://example.com/path?foo=bar"))

expect(response.code).to eq("200")
expect(transaction.span_recorder.spans.count).to eq(2)

request_span = transaction.span_recorder.spans.last
expect(request_span.op).to eq("http.client")
expect(request_span.origin).to eq("auto.http.net_http")
expect(request_span.start_timestamp).not_to be_nil
expect(request_span.timestamp).not_to be_nil
expect(request_span.start_timestamp).not_to eq(request_span.timestamp)
expect(request_span.description).to eq("GET http://example.com/path")
expect(request_span.data).to eq({
"http.response.status_code" => 200,
"url" => "http://example.com/path",
"http.request.method" => "GET",
"http.query" => "foo=bar"
})
end
end

context "with config.send_default_pii = false" do
before do
Sentry.configuration.send_default_pii = false
end
it "records the request's span with query string in data" do
stub_normal_response

it "records the request's span without query string" do
stub_normal_response
transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)

transaction = Sentry.start_transaction
Sentry.get_current_scope.set_span(transaction)
response = Net::HTTP.get_response(URI("http://example.com/path?foo=bar"))

response = Net::HTTP.get_response(URI("http://example.com/path?foo=bar"))

expect(response.code).to eq("200")
expect(transaction.span_recorder.spans.count).to eq(2)
expect(response.code).to eq("200")
expect(transaction.span_recorder.spans.count).to eq(2)

request_span = transaction.span_recorder.spans.last
expect(request_span.op).to eq("http.client")
expect(request_span.origin).to eq("auto.http.net_http")
expect(request_span.start_timestamp).not_to be_nil
expect(request_span.timestamp).not_to be_nil
expect(request_span.start_timestamp).not_to eq(request_span.timestamp)
expect(request_span.description).to eq("GET http://example.com/path")
expect(request_span.data).to eq({
"http.response.status_code" => 200,
"url" => "http://example.com/path",
"http.request.method" => "GET"
})
end
request_span = transaction.span_recorder.spans.last
expect(request_span.op).to eq("http.client")
expect(request_span.origin).to eq("auto.http.net_http")
expect(request_span.start_timestamp).not_to be_nil
expect(request_span.timestamp).not_to be_nil
expect(request_span.start_timestamp).not_to eq(request_span.timestamp)
expect(request_span.description).to eq("GET http://example.com/path")
expect(request_span.data).to eq({
"http.response.status_code" => 200,
"url" => "http://example.com/path",
"http.request.method" => "GET",
"http.query" => "foo=bar"
})
end

it "supports non-ascii characters in the path" do
Expand Down Expand Up @@ -348,7 +311,8 @@ def verify_spans(transaction)
expect(request_span.data).to eq({
"http.response.status_code" => 200,
"url" => "http://example.com/path",
"http.request.method" => "GET"
"http.request.method" => "GET",
"http.query" => "foo=bar"
})

request_span = transaction.span_recorder.spans[2]
Expand All @@ -361,7 +325,8 @@ def verify_spans(transaction)
expect(request_span.data).to eq({
"http.response.status_code" => 404,
"url" => "http://example.com/path",
"http.request.method" => "GET"
"http.request.method" => "GET",
"http.query" => "foo=bar"
})
end

Expand Down

0 comments on commit c6d6137

Please sign in to comment.