Skip to content

Commit 9e5ecfe

Browse files
committed
'*' allowed domain now allows no Referer/Origin to be set
1 parent 68867da commit 9e5ecfe

File tree

4 files changed

+45
-12
lines changed

4 files changed

+45
-12
lines changed

src/sentry/testutils/cases.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,22 @@ def _postWithHeader(self, data, key=None, secret=None):
116116
)
117117
return resp
118118

119+
def _getWithoutReferer(self, data, key=None):
120+
if key is None:
121+
key = self.projectkey.public_key
122+
123+
message = self._makeMessage(data)
124+
qs = {
125+
'sentry_version': '4',
126+
'sentry_client': 'raven-js/lol',
127+
'sentry_key': key,
128+
'sentry_data': message,
129+
}
130+
resp = self.client.get(
131+
'%s?%s' % (reverse('sentry-api-store', args=(self.project.pk,)), urllib.urlencode(qs))
132+
)
133+
return resp
134+
119135
def _getWithReferer(self, data, key=None):
120136
if key is None:
121137
key = self.projectkey.public_key
@@ -129,7 +145,7 @@ def _getWithReferer(self, data, key=None):
129145
}
130146
resp = self.client.get(
131147
'%s?%s' % (reverse('sentry-api-store', args=(self.project.pk,)), urllib.urlencode(qs)),
132-
HTTP_REFERER='http://lol.com/'
148+
HTTP_REFERER='http://getsentry.com/'
133149
)
134150
return resp
135151

src/sentry/utils/http.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,16 @@ def is_valid_origin(origin, project=None):
8787
- *.domain.com: matches domain.com and all subdomains, on any port
8888
- domain.com: matches domain.com on any port
8989
"""
90-
# we always run a case insensitive check
91-
origin = origin.lower()
92-
9390
allowed = get_origins(project)
9491
if '*' in allowed:
9592
return True
9693

9794
if not origin:
9895
return False
9996

97+
# we always run a case insensitive check
98+
origin = origin.lower()
99+
100100
# Fast check
101101
if origin in allowed:
102102
return True

src/sentry/web/api.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,15 @@ def _dispatch(self, request, project_id=None, *args, **kwargs):
175175
Raven.tags_context({'project': project.id})
176176

177177
origin = self.get_request_origin(request)
178-
if origin is not None:
179-
if not project:
180-
return HttpResponse('Your client must be upgraded for CORS support.')
181-
elif not is_valid_origin(origin, project):
182-
return HttpResponse('Invalid origin: %r' % origin, content_type='text/plain', status=400)
178+
if origin is not None and not project:
179+
return HttpResponse('Your client must be upgraded for CORS support.', content_type='text/plain', status=400)
183180

184181
# XXX: It seems that the OPTIONS call does not always include custom headers
185182
if request.method == 'OPTIONS':
186-
response = self.options(request, project)
183+
if is_valid_origin(origin, project):
184+
response = self.options(request, project)
185+
else:
186+
return HttpResponse('Invalid origin: %r' % origin, content_type='text/plain', status=400)
187187
else:
188188
try:
189189
auth_vars = self._parse_header(request, project)
@@ -213,8 +213,11 @@ def _dispatch(self, request, project_id=None, *args, **kwargs):
213213
if auth.version >= 3:
214214
if request.method == 'GET':
215215
# GET only requires an Origin/Referer check
216-
if origin is None:
217-
return HttpResponse('Missing required Origin or Referer header', status=400)
216+
if not is_valid_origin(origin, project):
217+
if origin is None:
218+
return HttpResponse('Missing required Origin or Referer header', content_type='text/plain', status=400)
219+
else:
220+
return HttpResponse('Invalid origin: %r' % origin, content_type='text/plain', status=400)
218221
else:
219222
# Version 3 enforces secret key for server side requests
220223
if not auth.secret_key:

tests/integration/tests.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from django.conf import settings as django_settings
99
from django.core.urlresolvers import reverse
10+
from django.test.utils import override_settings
1011
from django.utils import timezone
1112

1213
from raven import Client
@@ -161,6 +162,7 @@ def test_ungzipped_data(self):
161162
self.assertEquals(instance.site, 'not_a_real_site')
162163
self.assertEquals(instance.level, 40)
163164

165+
@override_settings(SENTRY_ALLOW_ORIGIN='getsentry.com')
164166
def test_correct_data_with_get(self):
165167
kwargs = {'message': 'hello', 'server_name': 'not_dcramer.local', 'level': 40, 'site': 'not_a_real_site'}
166168
resp = self._getWithReferer(kwargs)
@@ -171,6 +173,18 @@ def test_correct_data_with_get(self):
171173
self.assertEquals(instance.level, 40)
172174
self.assertEquals(instance.site, 'not_a_real_site')
173175

176+
@override_settings(SENTRY_ALLOW_ORIGIN='getsentry.com')
177+
def test_get_without_referer(self):
178+
kwargs = {'message': 'hello', 'server_name': 'not_dcramer.local', 'level': 40, 'site': 'not_a_real_site'}
179+
resp = self._getWithoutReferer(kwargs)
180+
self.assertEquals(resp.status_code, 400, resp.content)
181+
182+
@override_settings(SENTRY_ALLOW_ORIGIN='*')
183+
def test_get_without_referer_allowed(self):
184+
kwargs = {'message': 'hello', 'server_name': 'not_dcramer.local', 'level': 40, 'site': 'not_a_real_site'}
185+
resp = self._getWithoutReferer(kwargs)
186+
self.assertEquals(resp.status_code, 200, resp.content)
187+
174188
# def test_byte_sequence(self):
175189
# """
176190
# invalid byte sequence for encoding "UTF8": 0xedb7af

0 commit comments

Comments
 (0)