fix(audit): P0-P3 audit fixes — 838 ruff errors → 0, 30 F821 bugs fixed, 118 files changed
- P0: hooks.py 3-tuple fix, trigger_dispatcher Contract, contacts/plugin unregister_actions_by_owner - P0: 5 test files — check_permission mocks removed, hardcoded DB credential → env var - P1: attachment_service DmsFile via Contract helper, restore_registry/history_hooks dedup - P1: mail/plugin restore unregister, mcp_client datetime.now(UTC), saved_views/filters patterns - P1: ProtectedRoute fail-closed, 13 test assertion fixes (bcrypt, DB-URLs, SECRET_KEYs) - P2: deprecated notifications → post_system_message (3 files), forgejo Base, report_generator lazy import - P2: webhooks permissions, deps.py/roles.py plugin perms removed, import_export default - P2: address/tags/entity_links patterns removed, worker.py Contract-Umgehungen fixed - P2: 28 frontend TODOs (hardcoded constants, deprecated notification API) - P3: dead code, duplicates, deprecated imports, private attr, __import__ inline - P3: 8 frontend TODOs (LucideIcons, inline styles, XSS, i18n) - ruff: 838 → 0 (612 auto-fix + 246 manual + 27 F821 regression fix) - F821: 30 → 0 (AutomationDefinition, DmsFile, user_id, Path, Any, String) - Contract-Umgehungen: 2 neue gefunden (worker.py:169, worker.py:280) und gefixt
This commit is contained in:
@@ -17,6 +17,7 @@ from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy import and_, asc, desc, func, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
import app.plugins.builtins.mail.services as mail_services
|
||||
from app.core.db import get_db
|
||||
from app.core.storage import get_storage_backend
|
||||
from app.core.visibility import apply_visibility_filter, check_single_entity_access
|
||||
@@ -62,15 +63,11 @@ from app.plugins.builtins.mail.schemas import (
|
||||
TemplateSubstituteRequest,
|
||||
VacationConfig,
|
||||
)
|
||||
import app.plugins.builtins.mail.services as mail_services
|
||||
from app.plugins.builtins.mail.services import (
|
||||
MAX_ATTACHMENT_SIZE,
|
||||
_attachment_storage_path,
|
||||
_sanitize_filename,
|
||||
_save_attachment_to_storage,
|
||||
account_to_response,
|
||||
apply_rules_to_mail,
|
||||
attachment_to_response,
|
||||
create_mail_account,
|
||||
encrypt_password,
|
||||
folder_to_response,
|
||||
@@ -78,8 +75,6 @@ from app.plugins.builtins.mail.services import (
|
||||
get_account_password,
|
||||
imap_create_folder,
|
||||
imap_delete_folder,
|
||||
imap_delete_mail,
|
||||
imap_move_mail,
|
||||
imap_sync_account,
|
||||
import_pgp_private_key,
|
||||
import_pgp_public_key,
|
||||
@@ -239,7 +234,6 @@ async def create_account(
|
||||
):
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
account = await create_mail_account(
|
||||
db, tenant_id=tenant_id, user_id=user_id, data=data.model_dump()
|
||||
)
|
||||
@@ -291,12 +285,12 @@ async def delete_account(
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
acc_id = _parse_uuid(account_id, "account_id")
|
||||
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
||||
has_admin = await check_single_entity_access(
|
||||
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
||||
)
|
||||
if not has_admin:
|
||||
raise HTTPException(403, detail={"detail": "Only owner can delete", "code": "forbidden"})
|
||||
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
||||
await db.delete(account)
|
||||
|
||||
|
||||
@@ -311,7 +305,6 @@ async def assign_shared_users(
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
acc_id = _parse_uuid(account_id, "account_id")
|
||||
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
||||
has_admin = await check_single_entity_access(
|
||||
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
||||
)
|
||||
@@ -357,7 +350,6 @@ async def create_delegate(
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
acc_id = _parse_uuid(account_id, "account_id")
|
||||
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
||||
has_admin = await check_single_entity_access(
|
||||
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
||||
)
|
||||
@@ -402,7 +394,6 @@ async def create_send_permission(
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
acc_id = _parse_uuid(account_id, "account_id")
|
||||
account = await _get_account(db, acc_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
||||
has_admin = await check_single_entity_access(
|
||||
db, "mail_account", acc_id, user_id, tenant_id, "admin", is_system_admin
|
||||
)
|
||||
@@ -714,8 +705,6 @@ async def sync_folder(
|
||||
):
|
||||
"""Sync a single folder from IMAP server immediately."""
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
f_id = _parse_uuid(folder_id, "folder_id")
|
||||
folder = (
|
||||
await db.execute(
|
||||
@@ -724,7 +713,6 @@ async def sync_folder(
|
||||
).scalar_one_or_none()
|
||||
if not folder:
|
||||
raise HTTPException(404, detail={"detail": "Folder not found", "code": "not_found"})
|
||||
account = await _get_account(db, folder.account_id, tenant_id, user_id, is_system_admin=is_system_admin)
|
||||
result = await mail_services.imap_sync_folder(db, f_id, tenant_id)
|
||||
await db.flush()
|
||||
return result
|
||||
@@ -746,7 +734,6 @@ async def upload_attachment(
|
||||
"""
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
|
||||
# Rate limit — UPLOAD policy
|
||||
from app.core.rate_limit import RateLimitPolicy, check_rate_limit_policy
|
||||
@@ -919,7 +906,6 @@ async def create_template(
|
||||
):
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
template = MailTemplate(
|
||||
tenant_id=tenant_id,
|
||||
user_id=user_id,
|
||||
@@ -979,7 +965,6 @@ async def create_signature(
|
||||
):
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
acc_id = _parse_uuid(data.account_id, "account_id") if data.account_id else None
|
||||
sig = MailSignature(
|
||||
tenant_id=tenant_id,
|
||||
@@ -1131,7 +1116,6 @@ async def import_pgp_key(
|
||||
):
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
key_id, public_key_armored = import_pgp_private_key(data.private_key_armored, data.passphrase)
|
||||
encrypted_private = encrypt_password(data.private_key_armored)
|
||||
pgp_key = PgpKey(
|
||||
@@ -1152,7 +1136,6 @@ async def list_pgp_keys(
|
||||
):
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
keys = (
|
||||
(
|
||||
await db.execute(
|
||||
@@ -1204,7 +1187,6 @@ async def create_label(
|
||||
):
|
||||
tenant_id = uuid.UUID(current_user["tenant_id"])
|
||||
user_id = uuid.UUID(current_user["user_id"])
|
||||
is_system_admin = current_user.get("is_system_admin", False)
|
||||
label = MailLabel(tenant_id=tenant_id, name=data.name, color=data.color, user_id=user_id)
|
||||
db.add(label)
|
||||
await db.flush()
|
||||
@@ -1512,21 +1494,21 @@ async def create_event_from_mail(
|
||||
try:
|
||||
from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract
|
||||
_cal = get_calendar_contract()
|
||||
Calendar = _cal.Calendar
|
||||
CalendarEntry = _cal.CalendarEntry
|
||||
calendar = _cal.calendar
|
||||
calendar_entry = _cal.calendar_entry
|
||||
except ImportError:
|
||||
return {"created": False, "error": "Calendar plugin not available"}
|
||||
return {"created": False, "error": "calendar plugin not available"}
|
||||
cal_id = _parse_uuid(data.calendar_id, "calendar_id")
|
||||
calendar = (
|
||||
await db.execute(
|
||||
select(Calendar).where(and_(Calendar.id == cal_id, Calendar.tenant_id == tenant_id))
|
||||
select(calendar).where(and_(calendar.id == cal_id, calendar.tenant_id == tenant_id))
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if not calendar:
|
||||
raise HTTPException(404, detail={"detail": "Calendar not found", "code": "not_found"})
|
||||
raise HTTPException(404, detail={"detail": "calendar not found", "code": "not_found"})
|
||||
title = data.title or mail.subject
|
||||
description = data.description or (mail.body_text[:500] if mail.body_text else "")
|
||||
entry = CalendarEntry(
|
||||
entry = calendar_entry(
|
||||
tenant_id=tenant_id,
|
||||
calendar_id=cal_id,
|
||||
title=title,
|
||||
|
||||
Reference in New Issue
Block a user