mirror of
https://github.com/Sea-Haven-Industries/procurement-ingest.git
synced 2026-10-07 16:08:54 +00:00
fix(lambda): strip Sentry stack-frame locals
This commit is contained in:
parent
b9c6ec0f78
commit
620e380820
2 changed files with 80 additions and 2 deletions
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
Imported for side effect from each handler. ``init_sentry()`` is a no-op when
|
Imported for side effect from each handler. ``init_sentry()`` is a no-op when
|
||||||
``SENTRY_DSN`` is unset so pytest, local invokes, and a missing HCP var never
|
``SENTRY_DSN`` is unset so pytest, local invokes, and a missing HCP var never
|
||||||
talk to Sentry. ``before_send`` strips auth headers and drops request/extra
|
talk to Sentry. ``before_send`` strips auth headers, drops request/extra keys
|
||||||
keys that can hold MIME bodies, Bedrock prompts, or HMAC secret material.
|
that can hold MIME bodies, Bedrock prompts, or HMAC secret material, and
|
||||||
|
removes exception stack-frame locals. ``include_local_variables=False`` keeps
|
||||||
|
those locals out of the event in the first place.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
@ -63,6 +65,38 @@ def _scrub_headers(headers):
|
||||||
return headers
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
def _stacktraces(event):
|
||||||
|
traces = []
|
||||||
|
stacktrace = event.get("stacktrace")
|
||||||
|
if isinstance(stacktrace, dict):
|
||||||
|
traces.append(stacktrace)
|
||||||
|
for section in ("exception", "threads"):
|
||||||
|
container = event.get(section)
|
||||||
|
if not isinstance(container, dict):
|
||||||
|
continue
|
||||||
|
values = container.get("values")
|
||||||
|
if not isinstance(values, list):
|
||||||
|
continue
|
||||||
|
for item in values:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
continue
|
||||||
|
inner = item.get("stacktrace")
|
||||||
|
if isinstance(inner, dict):
|
||||||
|
traces.append(inner)
|
||||||
|
return traces
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_stack_locals(event):
|
||||||
|
"""Drop frame locals. Names like ``raw``/``item`` still hold MIME or secrets."""
|
||||||
|
for stacktrace in _stacktraces(event):
|
||||||
|
frames = stacktrace.get("frames")
|
||||||
|
if not isinstance(frames, list):
|
||||||
|
continue
|
||||||
|
for frame in frames:
|
||||||
|
if isinstance(frame, dict):
|
||||||
|
frame.pop("vars", None)
|
||||||
|
|
||||||
|
|
||||||
def _before_send(event, _hint):
|
def _before_send(event, _hint):
|
||||||
request = event.get("request")
|
request = event.get("request")
|
||||||
if isinstance(request, dict):
|
if isinstance(request, dict):
|
||||||
|
|
@ -78,6 +112,7 @@ def _before_send(event, _hint):
|
||||||
lower = str(key).lower()
|
lower = str(key).lower()
|
||||||
if any(needle in lower for needle in _DROP_EXTRA_NEEDLES):
|
if any(needle in lower for needle in _DROP_EXTRA_NEEDLES):
|
||||||
extra.pop(key, None)
|
extra.pop(key, None)
|
||||||
|
_strip_stack_locals(event)
|
||||||
return event
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -89,6 +124,7 @@ def init_sentry():
|
||||||
dsn=dsn,
|
dsn=dsn,
|
||||||
integrations=[AwsLambdaIntegration(timeout_warning=True)],
|
integrations=[AwsLambdaIntegration(timeout_warning=True)],
|
||||||
send_default_pii=False,
|
send_default_pii=False,
|
||||||
|
include_local_variables=False,
|
||||||
enable_logs=False,
|
enable_logs=False,
|
||||||
traces_sample_rate=0.0,
|
traces_sample_rate=0.0,
|
||||||
before_send=_before_send,
|
before_send=_before_send,
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ def test_set_dsn_inits_lambda_integration(sentry_mod, monkeypatch):
|
||||||
kwargs = mocked.call_args.kwargs
|
kwargs = mocked.call_args.kwargs
|
||||||
assert kwargs["dsn"] == "https://key@o1.ingest.sentry.io/1"
|
assert kwargs["dsn"] == "https://key@o1.ingest.sentry.io/1"
|
||||||
assert kwargs["send_default_pii"] is False
|
assert kwargs["send_default_pii"] is False
|
||||||
|
assert kwargs["include_local_variables"] is False
|
||||||
assert kwargs["enable_logs"] is False
|
assert kwargs["enable_logs"] is False
|
||||||
assert kwargs["traces_sample_rate"] == 0.0
|
assert kwargs["traces_sample_rate"] == 0.0
|
||||||
assert kwargs["before_send"] is sentry_mod._before_send
|
assert kwargs["before_send"] is sentry_mod._before_send
|
||||||
|
|
@ -100,3 +101,44 @@ def test_before_send_drops_body_prompt_and_secret_keys(sentry_mod):
|
||||||
assert "bedrock_prompt" not in out["extra"]
|
assert "bedrock_prompt" not in out["extra"]
|
||||||
assert "hmac_secret" not in out["extra"]
|
assert "hmac_secret" not in out["extra"]
|
||||||
assert out["extra"]["po_number"] == "123"
|
assert out["extra"]["po_number"] == "123"
|
||||||
|
|
||||||
|
|
||||||
|
def test_before_send_drops_exception_and_thread_frame_locals(sentry_mod):
|
||||||
|
event = {
|
||||||
|
"exception": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"stacktrace": {
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"function": "handler",
|
||||||
|
"vars": {
|
||||||
|
"raw_email": "From: attacker",
|
||||||
|
"SecretString": "aabbcc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"threads": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"stacktrace": {
|
||||||
|
"frames": [
|
||||||
|
{"function": "extract_with_claude", "vars": {"prompt": "EXTRACT"}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stacktrace": {
|
||||||
|
"frames": [{"function": "save_work_order", "vars": {"item": {"wo": 1}}}]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
out = sentry_mod._before_send(event, {})
|
||||||
|
assert "vars" not in out["exception"]["values"][0]["stacktrace"]["frames"][0]
|
||||||
|
assert "vars" not in out["threads"]["values"][0]["stacktrace"]["frames"][0]
|
||||||
|
assert "vars" not in out["stacktrace"]["frames"][0]
|
||||||
|
assert out["exception"]["values"][0]["stacktrace"]["frames"][0]["function"] == "handler"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue