feat: security hardening — SSRF guard, template sandbox timeout, webhook log prune, auth & backup polish
- Add outbound URL validation (SSRF) for webhook/Discord/Slack/ntfy/Matrix dispatch - Template renderer: input/output caps and thread-based render timeout - Webhook log filter: strip Authorization/signature/token-like headers; atomic prune - Auth/JWT/backup/config tightening; misc frontend UX fixes
This commit is contained in:
@@ -18,8 +18,23 @@ logger = logging.getLogger(__name__)
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_IDENT_RE = __import__("re").compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
||||
|
||||
|
||||
def _assert_ident(ident: str, kind: str = "identifier") -> str:
|
||||
"""Guard against SQL injection in dynamically interpolated identifiers.
|
||||
|
||||
All table/column names flow through here before being embedded into f-strings,
|
||||
so attacker-controlled values cannot break out even if they reach this layer.
|
||||
"""
|
||||
if not isinstance(ident, str) or not _IDENT_RE.match(ident):
|
||||
raise ValueError(f"Unsafe {kind}: {ident!r}")
|
||||
return ident
|
||||
|
||||
|
||||
async def _has_column(conn, table: str, column: str) -> bool:
|
||||
"""Check if a column exists in a SQLite table."""
|
||||
_assert_ident(table, "table")
|
||||
cols = await conn.run_sync(
|
||||
lambda sync_conn: [
|
||||
row[1]
|
||||
@@ -1187,3 +1202,15 @@ async def migrate_notification_slot_locale(engine: AsyncEngine) -> None:
|
||||
"Merged system notification template configs for %s (EN=%d, RU=%d) into %d",
|
||||
provider_type, en_id, ru_id, en_id,
|
||||
)
|
||||
|
||||
|
||||
async def migrate_user_token_version(engine: AsyncEngine) -> None:
|
||||
"""Add token_version column to user for JWT revocation on password change."""
|
||||
async with engine.begin() as conn:
|
||||
if not await _has_table(conn, "user"):
|
||||
return
|
||||
if not await _has_column(conn, "user", "token_version"):
|
||||
await conn.execute(
|
||||
text("ALTER TABLE user ADD COLUMN token_version INTEGER NOT NULL DEFAULT 1")
|
||||
)
|
||||
logger.info("Added token_version column to user table")
|
||||
|
||||
@@ -19,6 +19,7 @@ class User(SQLModel, table=True):
|
||||
username: str = Field(index=True, unique=True)
|
||||
hashed_password: str
|
||||
role: str = Field(default="user")
|
||||
token_version: int = Field(default=1)
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user