Files
rengine/api/app/core/ratelimit.py
T
Yogesh OjhaandClaude Opus 5 66c7501558 fix(ui): the interface obeyed none of the style rules the AI prompt enforces
reports/narrate/ai.py instructs the model: no marketing language, no hedging,
no filler, never an em dash, never a rhetorical question. The product broke all
three. There was a house style in this repository and it applied only to the
machine.

The activity feed printed Python dictionary keys. stage_count_summary rendered
key.replace('_', ' ') over StageResult.counts, so a live scan read "12 waf,
340 checked", "88 ptr", "412 http assets", "56 alive". _STAGE_COUNT_LABELS now
covers all 31 count keys with singular and plural, and an unlabelled key is
dropped rather than printed raw, so a new stage can never leak one again.

Errors had three registers: "Failed to X" (121), "Could not X" (28), and
"Couldn't X - try again" (6), alongside a passive form the result tables already
used. All of it is now "<Subject> could not be <verbed>", which names the thing
that failed before the failure. The rate limiter reads the key's TTL and says
how long to wait instead of "Please wait a moment".

Onboarding fired confetti and a party popper, opened with "Let's get your
instance set up", and asked an operator to tick "I've saved my backup codes".
confetti.ts is deleted.

Casing was split down the middle: the sidebar group read "Attack surface" while
the item under it read "Scheduled Scans", and page headings restated the nav in
Title Case. Sentence case throughout now, including SURFACE_LABELS, so the
backend and its mirror agree.

The KEV sentence in generated reports claimed a listing "means working exploit
code is public". It means exploitation has been observed in the wild. That one
printed under a client's letterhead.

Also: 18 "This action cannot be undone" in three spellings removed in favour of
the concrete consequence; every em dash, contraction and "Please" out of user
copy; surface vocabulary applied to alerts and the notification settings, not
only the result tables; the "X, not Y" antithesis thinned from ten uses to the
two that carry a real distinction; advice copy ("fix first", "worth a look",
"Add to renewal watchlist", which named a feature that does not exist) reduced
to the fact.

Nine rules are written up in the audit linked below and hold for new copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LM9TbCGhGb4s6nag6PsuaT
2026-09-06 18:26:29 +05:30

70 lines
2.0 KiB
Python

import logging
import redis.asyncio as aioredis
from fastapi import HTTPException, status
from app.config import settings
logger = logging.getLogger(__name__)
_redis: aioredis.Redis | None = None
def _client() -> aioredis.Redis:
global _redis # noqa: PLW0603
if _redis is None:
_redis = aioredis.from_url(settings.redis_url, decode_responses=True)
return _redis
async def too_many_attempts(key: str, *, limit: int) -> None:
try:
raw = await _client().get(key)
except Exception as exc:
logger.warning("rate limiter read unavailable for %s: %s", key, exc)
return
if raw is not None and int(raw) >= limit:
try:
ttl = await _client().ttl(key)
except Exception:
ttl = -1
wait = f" Try again in {ttl} seconds." if ttl and ttl > 0 else ""
raise HTTPException(
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
detail=f"Too many attempts.{wait}",
)
async def record_failure(key: str, *, window_seconds: int) -> None:
try:
pipe = _client().pipeline(transaction=True)
pipe.incr(key)
pipe.expire(key, window_seconds, nx=True)
await pipe.execute()
except Exception as exc:
logger.warning("rate limiter write unavailable for %s: %s", key, exc)
async def clear_failures(key: str) -> None:
try:
await _client().delete(key)
except Exception as exc:
logger.warning("rate limiter clear unavailable for %s: %s", key, exc)
async def revoke_token(jti: str, ttl_seconds: int) -> None:
if ttl_seconds <= 0:
return
try:
await _client().set(f"revoked:jti:{jti}", "1", ex=ttl_seconds)
except Exception as exc:
logger.warning("token revoke unavailable for %s: %s", jti, exc)
async def is_token_revoked(jti: str) -> bool:
try:
return await _client().exists(f"revoked:jti:{jti}") == 1
except Exception as exc:
logger.warning("token revoke check unavailable for %s: %s", jti, exc)
return False