harden(mail): address PR #935 round-4 review
No blockers raised; hardening + clarity: - client/mail.py: URL-encode the caller-supplied attachment_id (quote(..., safe="")) — defense-in-depth against path traversal. - server/mail.py: measure attachment content in UTF-8 bytes (not characters) for the size cap and the sentinel message. - scanner.py: bound _mail_cap_logged (insertion-ordered dict + oldest-first eviction at 50k, mirroring _consent_backstop_done) so the cap-log dedup set can't leak in a long-running multi-tenant process; reword the cap log to not imply MAIL_SCAN_MAX_PER_MAILBOX is operator-tunable (it's the Mail OCS max). - models/mail.py: comment why GetAttachmentResponse doesn't nest MailAttachment (different OCS endpoint shape). - mail_content.py: document format_mail_addresses' empty-entry skip contract. _potentially_deleted doc_type-in-key remains tracked as Deck #376 (pre-existing cross-cutting; reviewer confirmed deferral). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
891d07db12
commit
0856d59956
@@ -14,6 +14,7 @@ OCS API controllers (Mail 5.x / Nextcloud 32+).
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
|
||||
from httpx import HTTPStatusError, RequestError, Response
|
||||
|
||||
@@ -188,5 +189,10 @@ class MailClient(BaseNextcloudClient):
|
||||
Returns:
|
||||
Attachment object with name, mime, size, content.
|
||||
"""
|
||||
data = await self._ocs_get(f"/message/{message_id}/attachment/{attachment_id}")
|
||||
# URL-encode the caller-supplied attachment id (defense-in-depth: keeps
|
||||
# a value like "../.." from being normalised into a different path).
|
||||
safe_attachment_id = quote(attachment_id, safe="")
|
||||
data = await self._ocs_get(
|
||||
f"/message/{message_id}/attachment/{safe_attachment_id}"
|
||||
)
|
||||
return data or {}
|
||||
|
||||
Reference in New Issue
Block a user