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

chore: surface event if proxy token mismatches #14499

Merged
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
27 changes: 27 additions & 0 deletions tests/unit/utils/test_wsgi.py
Expand Up @@ -12,6 +12,7 @@

import pretend
import pytest
import sentry_sdk

from sqlalchemy import type_coerce
from sqlalchemy.dialects.postgresql import INET
Expand Down Expand Up @@ -43,6 +44,32 @@ def test_skips_headers(self):
assert resp is response
assert app.calls == [pretend.call({}, start_response)]

def test_token_mismatch_sends_sentry(self, monkeypatch):
"""In the event someone submits the WAREHOUSE_TOKEN header with an
incorrect value, we send a Sentry.
"""
mock_set_context = pretend.call_recorder(lambda *a, **kw: None)
monkeypatch.setattr(sentry_sdk, "set_context", mock_set_context)
mock_capture_message = pretend.call_recorder(lambda *a, **kw: None)
monkeypatch.setattr(sentry_sdk, "capture_message", mock_capture_message)

response = pretend.stub()
app = pretend.call_recorder(lambda e, s: response)

environ = {"HTTP_WAREHOUSE_TOKEN": "NOPE"}
start_response = pretend.stub()

resp = wsgi.ProxyFixer(app, token="1234", ip_salt="pepa")(
environ, start_response
)

assert resp is response
assert app.calls == [pretend.call({}, start_response)]
assert mock_set_context.calls == [pretend.call("ProxyFixer", {"token": "NOPE"})]
assert mock_capture_message.calls == [
pretend.call("Invalid Proxy Token", level="warning")
]

def test_accepts_warehouse_headers(self):
response = pretend.stub()
app = pretend.call_recorder(lambda e, s: response)
Expand Down
15 changes: 15 additions & 0 deletions warehouse/utils/wsgi.py
Expand Up @@ -16,6 +16,8 @@

from typing import TYPE_CHECKING

import sentry_sdk

from sqlalchemy import type_coerce
from sqlalchemy.dialects.postgresql import INET
from sqlalchemy.exc import NoResultFound
Expand Down Expand Up @@ -66,11 +68,24 @@ def __call__(self, environ, start_response):
}

host = environ.get("HTTP_WAREHOUSE_HOST", "")

# If we're not getting headers from a trusted third party via the
# specialized Warehouse-* headers, then we'll fall back to looking at
# X-Forwarded-* headers, assuming that whatever we have in front of us
# will strip invalid ones.
else:
# If there IS a token, but it doesn't match, then tell us about it.
miketheman marked this conversation as resolved.
Show resolved Hide resolved
if request_token is not None and not hmac.compare_digest(
self.token, request_token
):
sentry_sdk.set_context(
self.__class__.__name__, {"token": request_token}
)
sentry_sdk.capture_message(
"Invalid Proxy Token",
level="warning",
)

proto = environ.get("HTTP_X_FORWARDED_PROTO", "")

# Special case: if we don't see a X-Forwarded-For, this may be a local
Expand Down