Skip to content

Commit

Permalink
Always remove Django session related cookies. (#1842)
Browse files Browse the repository at this point in the history
* Always remove Django session related cookies.
  • Loading branch information
antonpirker committed Jan 20, 2023
1 parent 032ea57 commit d515233
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 3 deletions.
2 changes: 2 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
DEFAULT_QUEUE_SIZE = 100
DEFAULT_MAX_BREADCRUMBS = 100

SENSITIVE_DATA_SUBSTITUTE = "[Filtered]"


class INSTRUMENTER:
SENTRY = "sentry"
Expand Down
20 changes: 17 additions & 3 deletions sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
import weakref

from sentry_sdk._types import MYPY
from sentry_sdk.consts import OP
from sentry_sdk.consts import OP, SENSITIVE_DATA_SUBSTITUTE
from sentry_sdk.hub import Hub, _should_send_default_pii
from sentry_sdk.scope import add_global_event_processor
from sentry_sdk.serializer import add_global_repr_processor
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TRANSACTION_SOURCE_URL
from sentry_sdk.tracing_utils import record_sql_queries
from sentry_sdk.utils import (
AnnotatedValue,
HAS_REAL_CONTEXTVARS,
CONTEXTVARS_ERROR_MESSAGE,
logger,
Expand All @@ -28,6 +29,7 @@

try:
from django import VERSION as DJANGO_VERSION
from django.conf import settings as django_settings
from django.core import signals

try:
Expand Down Expand Up @@ -476,8 +478,20 @@ def env(self):
return self.request.META

def cookies(self):
# type: () -> Dict[str, str]
return self.request.COOKIES
# type: () -> Dict[str, Union[str, AnnotatedValue]]
privacy_cookies = [
django_settings.CSRF_COOKIE_NAME,
django_settings.SESSION_COOKIE_NAME,
]

clean_cookies = {} # type: Dict[str, Union[str, AnnotatedValue]]
for (key, val) in self.request.COOKIES.items():
if key in privacy_cookies:
clean_cookies[key] = SENSITIVE_DATA_SUBSTITUTE
else:
clean_cookies[key] = val

return clean_cookies

def raw_data(self):
# type: () -> bytes
Expand Down
18 changes: 18 additions & 0 deletions sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,24 @@ def removed_because_over_size_limit(cls):
},
)

@classmethod
def substituted_because_contains_sensitive_data(cls):
# type: () -> AnnotatedValue
"""The actual value was removed because it contained sensitive information."""
from sentry_sdk.consts import SENSITIVE_DATA_SUBSTITUTE

return AnnotatedValue(
value=SENSITIVE_DATA_SUBSTITUTE,
metadata={
"rem": [ # Remark
[
"!config", # Because of SDK configuration (in this case the config is the hard coded removal of certain django cookies)
"s", # The fields original value was substituted
]
]
},
)


if MYPY:
from typing import TypeVar
Expand Down
103 changes: 103 additions & 0 deletions tests/integrations/django/test_data_scrubbing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from functools import partial
import pytest
import pytest_django

from werkzeug.test import Client

from sentry_sdk.integrations.django import DjangoIntegration

from tests.integrations.django.myapp.wsgi import application

try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse


# Hack to prevent from experimental feature introduced in version `4.3.0` in `pytest-django` that
# requires explicit database allow from failing the test
pytest_mark_django_db_decorator = partial(pytest.mark.django_db)
try:
pytest_version = tuple(map(int, pytest_django.__version__.split(".")))
if pytest_version > (4, 2, 0):
pytest_mark_django_db_decorator = partial(
pytest.mark.django_db, databases="__all__"
)
except ValueError:
if "dev" in pytest_django.__version__:
pytest_mark_django_db_decorator = partial(
pytest.mark.django_db, databases="__all__"
)
except AttributeError:
pass


@pytest.fixture
def client():
return Client(application)


@pytest.mark.forked
@pytest_mark_django_db_decorator()
def test_scrub_django_session_cookies_removed(
sentry_init,
client,
capture_events,
):
sentry_init(integrations=[DjangoIntegration()], send_default_pii=False)
events = capture_events()
client.set_cookie("localhost", "sessionid", "123")
client.set_cookie("localhost", "csrftoken", "456")
client.set_cookie("localhost", "foo", "bar")
client.get(reverse("view_exc"))

(event,) = events
assert "cookies" not in event["request"]


@pytest.mark.forked
@pytest_mark_django_db_decorator()
def test_scrub_django_session_cookies_filtered(
sentry_init,
client,
capture_events,
):
sentry_init(integrations=[DjangoIntegration()], send_default_pii=True)
events = capture_events()
client.set_cookie("localhost", "sessionid", "123")
client.set_cookie("localhost", "csrftoken", "456")
client.set_cookie("localhost", "foo", "bar")
client.get(reverse("view_exc"))

(event,) = events
assert event["request"]["cookies"] == {
"sessionid": "[Filtered]",
"csrftoken": "[Filtered]",
"foo": "bar",
}


@pytest.mark.forked
@pytest_mark_django_db_decorator()
def test_scrub_django_custom_session_cookies_filtered(
sentry_init,
client,
capture_events,
settings,
):
settings.SESSION_COOKIE_NAME = "my_sess"
settings.CSRF_COOKIE_NAME = "csrf_secret"

sentry_init(integrations=[DjangoIntegration()], send_default_pii=True)
events = capture_events()
client.set_cookie("localhost", "my_sess", "123")
client.set_cookie("localhost", "csrf_secret", "456")
client.set_cookie("localhost", "foo", "bar")
client.get(reverse("view_exc"))

(event,) = events
assert event["request"]["cookies"] == {
"my_sess": "[Filtered]",
"csrf_secret": "[Filtered]",
"foo": "bar",
}

0 comments on commit d515233

Please sign in to comment.