From fd435fd8f09471d9322d623b8d701715bf98ea38 Mon Sep 17 00:00:00 2001 From: NossXBT Date: Wed, 22 Jul 2026 15:39:30 +0700 Subject: [PATCH] fix(security): cap Sentry sampling via env defaults Replace 100% traces_sample_rate and unstable _experiments profiling with SENTRY_TRACES_SAMPLE_RATE / SENTRY_PROFILES_SAMPLE_RATE (default 0.1). Log active rates at startup and document both env vars. Closes #200 Signed-off-by: NossXBT --- docs/environment_variables.md | 2 ++ quantara/web_app/api/main.py | 27 ++++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/docs/environment_variables.md b/docs/environment_variables.md index e9620f75..62d5b4c0 100644 --- a/docs/environment_variables.md +++ b/docs/environment_variables.md @@ -36,6 +36,8 @@ will fail to start in production if any required variable is missing. | `RATE_LIMIT_WRITE` | Limit for mutation endpoints — create/close position, auth (default `5/minute`) | | `RATE_LIMIT_USER_DATA` | Limit for user-specific data endpoints — dashboard, positions (default `30/minute`) | | `RATE_LIMIT_READ` | Limit for read-only endpoints — multipliers, leaderboard (default `100/minute`) | +| `SENTRY_TRACES_SAMPLE_RATE` | Fraction of transactions sent to Sentry (default `0.1`). Applied as the base rate for `traces_sampler`. | +| `SENTRY_PROFILES_SAMPLE_RATE` | Fraction of profiled transactions (default `0.1`). Replaces the old `_experiments` continuous profiling flag. | ## Rate limiting diff --git a/quantara/web_app/api/main.py b/quantara/web_app/api/main.py index ae8b6a71..9c5375bc 100644 --- a/quantara/web_app/api/main.py +++ b/quantara/web_app/api/main.py @@ -64,17 +64,24 @@ def get_cors_origins() -> list[str]: return [origin for origin in origins if origin] or DEFAULT_CORS_ORIGINS def custom_traces_sampler(sampling_context): + """Scale the base SENTRY_TRACES_SAMPLE_RATE by route importance. + + Returns a sample rate in [0, 1]. Critical auth/bug-report paths keep the + full configured rate; health is heavily downsampled; mutations are half; + everything else is 5% of the configured rate. Never exceeds the env cap. + """ + base = float(os.getenv("SENTRY_TRACES_SAMPLE_RATE", "0.1")) asgi_scope = sampling_context.get("asgi_scope", {}) path = asgi_scope.get("path", "") method = asgi_scope.get("method", "") if path in ("/api/save-bug-report", "/api/auth/connect"): - return 1.0 + return base if path == "/health": - return 0.005 + return min(base, 0.005) if method in ("POST", "PUT", "PATCH", "DELETE"): - return 0.5 - return 0.05 + return min(base, base * 0.5) + return min(base, base * 0.05) @asynccontextmanager async def lifespan(app: FastAPI): @@ -96,12 +103,18 @@ async def lifespan(app: FastAPI): # Initialize Sentry SDK if in production if os.getenv("ENV_VERSION") == "PROD": import sentry_sdk + + traces_sample_rate = float(os.getenv("SENTRY_TRACES_SAMPLE_RATE", "0.1")) + profiles_sample_rate = float(os.getenv("SENTRY_PROFILES_SAMPLE_RATE", "0.1")) + logger.info( + "sentry_init", + traces_sample_rate=traces_sample_rate, + profiles_sample_rate=profiles_sample_rate, + ) sentry_sdk.init( dsn=os.getenv("SENTRY_DSN"), traces_sampler=custom_traces_sampler, - _experiments={ - "continuous_profiling_auto_start": True, - }, + profiles_sample_rate=profiles_sample_rate, ) yield