chore(lint): auto-fix 253 of 283 ruff issues (F401, I001, E402, RUF100, UP037, SIM105)
Some checks failed
CI / lint (push) Failing after 2s
CI / typecheck (push) Failing after 2s
CI / test (push) Failing after 2s
CI / Secret scan (gitleaks) (push) Failing after 1s
CI / Security audit (bandit) (push) Failing after 2s

Mass ruff auto-fix:
  - ruff check --fix: 109 issues fixed (F401 unused imports,
    I001 unsorted imports, UP037 quoted annotations, SIM105
    suppressible exception, RUF100 unused-noqa)
  - ruff check --fix --unsafe-fixes: 22 additional issues
  - ruff format: 70 files reformatted
  - Manual pass: fix 16 misplaced import httpx lines
  - Manual pass: fix remaining E402 (import-after-docstring)

Result: 283 errors -> 30 errors.

The remaining 30 are real issues that need manual review:
  5 F401 unused-import (likely auto-generated stubs)
  5 F821 undefined-name (real bugs in code that references
    redis/pydantic/LLMRegistry without imports)
  3 BLE001 (the compliance LLM fallback is intentional; the
    other two are real)
  3 RUF012 mutable-class-default
  3 SIM105, 3 SIM117, 2 E722, 2 E741
  1 B007, 1 B025, 1 E402, 1 RUF200 (pyproject.toml issue)

Tests: 436/437 pass (1 pre-existing SSE sandbox failure).
format check + import sort: now clean.
make ci: still gated on the 30 remaining real issues.
Follow-up: triage the 30 issues file-by-file.
This commit is contained in:
Crypto Rug Munch 2026-07-02 21:51:25 +02:00
parent e60a62a07a
commit a7c30b12cd
85 changed files with 2374 additions and 1071 deletions

59
auth.py
View file

@ -20,6 +20,7 @@ logger = logging.getLogger(__name__)
# Try to import JWT library
try:
import jwt
_has_jwt = True
except ImportError:
_has_jwt = False
@ -30,8 +31,11 @@ except ImportError:
# an unset JWT_SECRET cannot accidentally sign tokens in a way that survives restart.
try:
from secrets_backend import get_secret
JWT_SECRET = get_secret("jwt_secret") or os.getenv("PRY_JWT_SECRET") or (
"ephemeral-" + secrets.token_hex(32)
JWT_SECRET = (
get_secret("jwt_secret")
or os.getenv("PRY_JWT_SECRET")
or ("ephemeral-" + secrets.token_hex(32))
)
except ImportError:
JWT_SECRET = os.getenv("PRY_JWT_SECRET") or ("ephemeral-" + secrets.token_hex(32))
@ -51,7 +55,8 @@ class AuthManager:
self._api_keys: dict[str, dict[str, Any]] = {}
def hash_password(self, password: str, salt: str | None = None) -> tuple[str, str]:
if salt is None: salt = secrets.token_hex(16)
if salt is None:
salt = secrets.token_hex(16)
h = hashlib.pbkdf2_hmac("sha256", password.encode(), salt.encode(), 100000)
return h.hex(), salt
@ -63,21 +68,31 @@ class AuthManager:
user_id = secrets.token_hex(12)
pwd_hash, salt = self.hash_password(password)
user = {
"id": user_id, "email": email, "password_hash": pwd_hash, "salt": salt,
"role": role, "created_at": datetime.now(UTC).isoformat(),
"active": True, "api_keys": [],
"id": user_id,
"email": email,
"password_hash": pwd_hash,
"salt": salt,
"role": role,
"created_at": datetime.now(UTC).isoformat(),
"active": True,
"api_keys": [],
}
self._users[user_id] = user
return user
def create_api_key(self, user_id: str, name: str = "default", rate_limit_rpm: int = DEFAULT_RATE_LIMIT_RPM) -> str:
def create_api_key(
self, user_id: str, name: str = "default", rate_limit_rpm: int = DEFAULT_RATE_LIMIT_RPM
) -> str:
key = "pry_" + secrets.token_urlsafe(API_KEY_LENGTH)
key_hash = hashlib.sha256(key.encode()).hexdigest()
api_key = {
"key_hash": key_hash, "user_id": user_id, "name": name,
"key_hash": key_hash,
"user_id": user_id,
"name": name,
"rate_limit_rpm": rate_limit_rpm,
"created_at": datetime.now(UTC).isoformat(),
"last_used": None, "use_count": 0,
"last_used": None,
"use_count": 0,
}
self._api_keys[key_hash] = api_key
if user_id in self._users:
@ -95,7 +110,8 @@ class AuthManager:
return api_key
def check_rate_limit(self, api_key: str) -> tuple[bool, int]:
if not api_key: return True, 0
if not api_key:
return True, 0
now = time.time()
rl = self._rate_limits.setdefault(api_key, {"window_start": now, "count": 0})
if now - rl["window_start"] > 60:
@ -103,7 +119,11 @@ class AuthManager:
rl["count"] = 0
rl["count"] += 1
api_key_data = self.verify_api_key(api_key)
limit = api_key_data.get("rate_limit_rpm", DEFAULT_RATE_LIMIT_RPM) if api_key_data else DEFAULT_RATE_LIMIT_RPM
limit = (
api_key_data.get("rate_limit_rpm", DEFAULT_RATE_LIMIT_RPM)
if api_key_data
else DEFAULT_RATE_LIMIT_RPM
)
remaining = max(0, limit - rl["count"])
return rl["count"] <= limit, remaining
@ -112,15 +132,21 @@ class AuthManager:
# Fallback: simple base64 token (NOT for production)
payload = {"sub": user_id, "role": role, "exp": time.time() + JWT_EXPIRY_HOURS * 3600}
return "pry_jwt." + base64_encode(json.dumps(payload))
payload = {"sub": user_id, "role": role, "exp": datetime.now(UTC) + timedelta(hours=JWT_EXPIRY_HOURS)}
payload = {
"sub": user_id,
"role": role,
"exp": datetime.now(UTC) + timedelta(hours=JWT_EXPIRY_HOURS),
}
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
def verify_jwt(self, token: str) -> dict[str, Any] | None:
if not _has_jwt:
try:
if not token.startswith("pry_jwt."): return None
if not token.startswith("pry_jwt."):
return None
payload = json.loads(base64_decode(token[8:]))
if payload.get("exp", 0) < time.time(): return None
if payload.get("exp", 0) < time.time():
return None
return payload
except (json.JSONDecodeError, ValueError):
return None
@ -132,11 +158,14 @@ class AuthManager:
def base64_encode(s: str) -> str:
import base64
return base64.urlsafe_b64encode(s.encode()).decode().rstrip("=")
def base64_decode(s: str) -> str:
import base64
padding = 4 - len(s) % 4
if padding != 4: s += "=" * padding
if padding != 4:
s += "=" * padding
return base64.urlsafe_b64decode(s.encode()).decode()