sentry: Provide a server-side tunnel.

Some well-intentioned adblockers also block Sentry client-side error
reporting.  Provide an endpoint on the Zulip server which forwards to
the Sentry server, so that these requests are not blocked.
This commit is contained in:
Alex Vandiver 2023-03-24 15:00:46 +00:00 committed by Tim Abbott
parent 926cb84ca5
commit 93bfd3fb5f
5 changed files with 50 additions and 0 deletions

View File

@ -15,6 +15,7 @@ IGNORED_PHRASES = [
r"Botserver",
r"Cookie Bot",
r"DevAuthBackend",
r"DSN",
r"GCM",
r"GitHub",
r"Gravatar",

View File

@ -137,6 +137,8 @@ not_yet_fully_covered = [
"zerver/webhooks/teamcity/view.py",
"zerver/webhooks/travis/view.py",
"zerver/webhooks/zapier/view.py",
# This is hard to get test coverage for, and low value to do so
"zerver/views/sentry.py",
# Cannot have coverage, as tests run in a transaction
"zerver/lib/safe_session_cached_db.py",
"zerver/lib/singleton_bmemcached.py",

View File

@ -47,6 +47,7 @@ if (page_params.server_sentry_dsn) {
Sentry.init({
dsn: page_params.server_sentry_dsn,
environment: page_params.server_sentry_environment || "development",
tunnel: "/error_tracing",
release: "zulip-server@" + ZULIP_VERSION,
integrations: [

41
zerver/views/sentry.py Normal file
View File

@ -0,0 +1,41 @@
import urllib
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext as _
from django.views.decorators.csrf import csrf_exempt
from zerver.lib.exceptions import JsonableError
from zerver.lib.outgoing_http import OutgoingSession
from zerver.lib.validator import (
check_url,
to_wild_value,
)
class SentryTunnelSession(OutgoingSession):
def __init__(self) -> None:
super().__init__(role="sentry_tunnel", timeout=5)
@csrf_exempt
def sentry_tunnel(
request: HttpRequest,
) -> HttpResponse:
try:
envelope = request.body
header = to_wild_value("envelope", envelope.split(b"\n")[0].decode("utf-8"))
dsn = urllib.parse.urlparse(header["dsn"].tame(check_url))
except Exception:
raise JsonableError(_("Invalid request format"))
if dsn.geturl() != settings.SENTRY_FRONTEND_DSN:
raise JsonableError(_("Invalid DSN"))
assert dsn.hostname
project_id = dsn.path.strip("/")
url = dsn._replace(netloc=dsn.hostname, path=f"/api/{project_id}/envelope/").geturl()
SentryTunnelSession().post(
url=url, data=envelope, headers={"Content-Type": "application/x-sentry-envelope"}
).raise_for_status()
return HttpResponse(status=200)

View File

@ -137,6 +137,7 @@ from zerver.views.report import (
report_send_times,
report_unnarrow_times,
)
from zerver.views.sentry import sentry_tunnel
from zerver.views.storage import get_storage, remove_storage, update_storage
from zerver.views.streams import (
add_default_stream,
@ -790,6 +791,10 @@ urls += [
path("scim/v2/", include("django_scim.urls", namespace="scim")),
]
# Front-end Sentry requests tunnel through the server, if enabled
if settings.SENTRY_FRONTEND_DSN:
urls += [path("error_tracing", sentry_tunnel)]
# User documentation site
help_documentation_view = MarkdownDirectoryView.as_view(
template_name="zerver/documentation_main.html",