lawn-estimator-dev / tests /test_observability.py
TempuraML's picture
feat(app): Sentry error tracking, env-gated + secret-scrubbed (obs MVP)
4e35ad0
Raw
History Blame Contribute Delete
2.5 kB
"""The Sentry secret-scrubber (observability.scrub_text / scrub_event).
The scrubber is the security-critical part of error tracking: the pipeline puts the Google key
in Static Maps URLs, so an unscrubbed exception would ship that key to a third party. These
tests pin that it's redacted everywhere an event can carry a string. No sentry-sdk, no network.
"""
from __future__ import annotations
from lawn_estimator.observability import scrub_event, scrub_text
def test_redacts_google_key_in_a_url():
url = "https://maps.googleapis.com/maps/api/staticmap?center=41.2,-96&zoom=20&key=AIzaSyABC123secret"
out = scrub_text(url)
assert "AIzaSyABC123secret" not in out
assert "key=REDACTED" in out
assert "zoom=20" in out # non-secret params are untouched
def test_redacts_common_secret_param_names():
for param in ["api_key", "apikey", "token", "access_token", "password", "client_secret",
"signature", "sig", "auth"]:
s = f"http://x/y?{param}=SUPERSECRETVALUE&ok=1"
out = scrub_text(s)
assert "SUPERSECRETVALUE" not in out, param
assert "ok=1" in out
def test_redacts_bearer_tokens():
out = scrub_text("Authorization: Bearer abc.def.ghi-123")
assert "abc.def.ghi-123" not in out and "Bearer REDACTED" in out
def test_scrub_is_case_insensitive_on_the_key_name():
assert "SECRETV" not in scrub_text("http://x?KEY=SECRETV")
assert "SECRETV" not in scrub_text("http://x?Api_Key=SECRETV")
def test_leaves_clean_text_untouched():
clean = "measured 4200 sqft for 5534 Mayberry St; method=lidar+rgb confidence=high"
assert scrub_text(clean) == clean
def test_scrub_event_deep_walks_nested_structures():
event = {
"request": {"url": "https://maps.googleapis.com/x?key=AIzaLEAK1"},
"exception": {"values": [{"value": "GET https://api/x?token=LEAK2 failed"}]},
"breadcrumbs": [{"message": "hit https://a/b?password=LEAK3"}],
"extra": {"nested": ["plain", {"deep": "http://q?secret=LEAK4"}]},
"level": "error",
}
out = scrub_event(event)
blob = repr(out)
for leak in ("AIzaLEAK1", "LEAK2", "LEAK3", "LEAK4"):
assert leak not in blob, leak
assert out["level"] == "error" # structure/non-secret values preserved
def test_scrub_event_never_raises_on_odd_input():
# A malformed event must not crash the send path.
assert scrub_event({"a": 1, "b": None, "c": 3.5}) == {"a": 1, "b": None, "c": 3.5}