mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-01 02:32:21 +01:00
fix(security): harden Python service boundaries
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
"""Per-principal extraction directories for email attachments.
|
||||
|
||||
Shared by the HTTP email routes and the email MCP server, which both extract
|
||||
attachments under MAIL_ATTACHMENTS_DIR.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def attachment_scope_dir(root, folder, uid, *, owner, account_id) -> Path:
|
||||
"""Return the extraction directory for one message's attachments.
|
||||
|
||||
IMAP UIDs are small per-mailbox counters and folder names are server- or
|
||||
caller-supplied (`/`-delimited hierarchies, absolute or `..` segments), so
|
||||
neither may become a path. The directory is one hex segment derived from
|
||||
(owner, account, folder, uid): distinct principals and mailboxes never
|
||||
share it, and no input can steer it out of *root*. Containment is checked
|
||||
after resolution, so a symlinked entry cannot redirect it either.
|
||||
"""
|
||||
scope = json.dumps(
|
||||
[str(owner or ""), str(account_id or ""), str(folder or ""), str(uid or "")],
|
||||
ensure_ascii=False,
|
||||
)
|
||||
base = Path(root).resolve()
|
||||
target = (base / hashlib.sha256(scope.encode("utf-8")).hexdigest()[:32]).resolve()
|
||||
if target.parent != base:
|
||||
raise ValueError("attachment directory escapes the extraction root")
|
||||
return target
|
||||
Reference in New Issue
Block a user