Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add ClientOptions.SendDefaultPii #485

Merged
merged 3 commits into from Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,8 +1,11 @@
# Changelog

## Unreleased

- fix: Scope values should not override Event values (#446)
- feat: Extend User inteface by adding Data, Name and Segment (#483)
- feat: Make maximum amount of spans configurable (#460)
- feat: Add ClientOptions.SendDefaultPii #485
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to see the Sentry Go SDK evolving!

One suggestion: initialisms like "PII" are spelled with a consistent case as per Go community conventions https://github.com/golang/go/wiki/CodeReviewComments#initialisms.

The option could be called ClientOptions.SendDefaultPII.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I opened #490


## 0.14.0

Expand Down
3 changes: 3 additions & 0 deletions client.go
Expand Up @@ -134,6 +134,9 @@ type ClientOptions struct {
// and if applicable, caught errors type and value.
// If the match is found, then a whole event will be dropped.
IgnoreErrors []string
// If this flag is enabled, certain personally identifiable information (PII) is added by active integrations.
// By default, no such data is sent.
SendDefaultPii bool
// BeforeSend is called before error events are sent to Sentry.
// Use it to mutate the event or return nil to discard the event.
// See EventProcessor if you need to mutate transactions.
Expand Down
1 change: 1 addition & 0 deletions fasthttp/sentryfasthttp_test.go
Expand Up @@ -142,6 +142,7 @@ func TestIntegration(t *testing.T) {

eventsCh := make(chan *sentry.Event, len(tests))
err := sentry.Init(sentry.ClientOptions{
SendDefaultPii: true,
BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
eventsCh <- event
return event
Expand Down
1 change: 1 addition & 0 deletions http/sentryhttp_test.go
Expand Up @@ -156,6 +156,7 @@ func TestIntegration(t *testing.T) {

eventsCh := make(chan *sentry.Event, len(tests))
err := sentry.Init(sentry.ClientOptions{
SendDefaultPii: true,
BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
eventsCh <- event
return event
Expand Down
51 changes: 37 additions & 14 deletions interfaces.go
Expand Up @@ -28,6 +28,15 @@ const (
LevelFatal Level = "fatal"
)

func getSensitiveHeaders() map[string]bool {
return map[string]bool{
"Authorization": true,
"Cookie": true,
"X-Forwarded-For": true,
"X-Real-Ip": true,
}
}

// SdkInfo contains all metadata about about the SDK being used.
type SdkInfo struct {
Name string `json:"name,omitempty"`
Expand Down Expand Up @@ -156,23 +165,37 @@ func NewRequest(r *http.Request) *Request {
}
url := fmt.Sprintf("%s://%s%s", protocol, r.Host, r.URL.Path)

// We read only the first Cookie header because of the specification:
// https://tools.ietf.org/html/rfc6265#section-5.4
// When the user agent generates an HTTP request, the user agent MUST NOT
// attach more than one Cookie header field.
cookies := r.Header.Get("Cookie")

headers := make(map[string]string, len(r.Header))
for k, v := range r.Header {
headers[k] = strings.Join(v, ",")
}
headers["Host"] = r.Host

var cookies string
var env map[string]string
if addr, port, err := net.SplitHostPort(r.RemoteAddr); err == nil {
env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
headers := map[string]string{}

if client := CurrentHub().Client(); client != nil {
if client.Options().SendDefaultPii {
// We read only the first Cookie header because of the specification:
// https://tools.ietf.org/html/rfc6265#section-5.4
// When the user agent generates an HTTP request, the user agent MUST NOT
// attach more than one Cookie header field.
cookies = r.Header.Get("Cookie")

for k, v := range r.Header {
headers[k] = strings.Join(v, ",")
}

if addr, port, err := net.SplitHostPort(r.RemoteAddr); err == nil {
env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
}
}
} else {
sensitiveHeaders := getSensitiveHeaders()
for k, v := range r.Header {
if _, ok := sensitiveHeaders[k]; !ok {
headers[k] = strings.Join(v, ",")
}
}
}

headers["Host"] = r.Host

return &Request{
URL: url,
Method: r.Method,
Expand Down
49 changes: 46 additions & 3 deletions interfaces_test.go
Expand Up @@ -66,16 +66,34 @@ func TestUserMarshalJson(t *testing.T) {
}

func TestNewRequest(t *testing.T) {
currentHub.BindClient(&Client{
options: ClientOptions{
SendDefaultPii: true,
},
})
// Unbind the client afterwards, to not affect other tests
defer currentHub.stackTop().SetClient(nil)

const payload = `{"test_data": true}`
got := NewRequest(httptest.NewRequest("POST", "/test/?q=sentry", strings.NewReader(payload)))
r := httptest.NewRequest("POST", "/test/?q=sentry", strings.NewReader(payload))
r.Header.Add("Authorization", "Bearer 1234567890")
r.Header.Add("Cookie", "foo=bar")
r.Header.Add("X-Forwarded-For", "127.0.0.1")
r.Header.Add("X-Real-Ip", "127.0.0.1")

got := NewRequest(r)
want := &Request{
URL: "http://example.com/test/",
Method: "POST",
Data: "",
QueryString: "q=sentry",
Cookies: "",
Cookies: "foo=bar",
Headers: map[string]string{
"Host": "example.com",
"Authorization": "Bearer 1234567890",
"Cookie": "foo=bar",
"Host": "example.com",
"X-Forwarded-For": "127.0.0.1",
"X-Real-Ip": "127.0.0.1",
},
Env: map[string]string{
"REMOTE_ADDR": "192.0.2.1",
Expand All @@ -87,6 +105,31 @@ func TestNewRequest(t *testing.T) {
}
}

func TestNewRequestWithNoPii(t *testing.T) {
const payload = `{"test_data": true}`
r := httptest.NewRequest("POST", "/test/?q=sentry", strings.NewReader(payload))
r.Header.Add("Authorization", "Bearer 1234567890")
r.Header.Add("Cookie", "foo=bar")
r.Header.Add("X-Forwarded-For", "127.0.0.1")
r.Header.Add("X-Real-Ip", "127.0.0.1")

got := NewRequest(r)
want := &Request{
URL: "http://example.com/test/",
Method: "POST",
Data: "",
QueryString: "q=sentry",
Cookies: "",
Headers: map[string]string{
"Host": "example.com",
},
Env: nil,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("Request mismatch (-want +got):\n%s", diff)
}
}

func TestEventMarshalJSON(t *testing.T) {
event := NewEvent()
event.Spans = []*Span{{
Expand Down