mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-27 07:05:23 -04:00
fix(email): enforce MCP owner boundaries (#4335)
* fix(email): enforce MCP owner boundaries * fix(email): fail closed for unowned MCP fallback
This commit is contained in:
@@ -406,6 +406,54 @@ async def test_scheduled_email_routes_are_owner_scoped(tmp_path, monkeypatch):
|
||||
assert alice_rows["scheduled"] == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pending_agent_draft_routes_do_not_expose_ownerless_rows(tmp_path, monkeypatch):
|
||||
import routes.email_helpers as email_helpers
|
||||
import routes.email_routes as email_routes
|
||||
|
||||
db_path = tmp_path / "scheduled_emails.db"
|
||||
monkeypatch.setattr(email_helpers, "SCHEDULED_DB", db_path)
|
||||
monkeypatch.setattr(email_routes, "SCHEDULED_DB", db_path)
|
||||
email_helpers._init_scheduled_db()
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.executemany(
|
||||
"""
|
||||
INSERT INTO scheduled_emails
|
||||
(id, to_addr, subject, body, attachments, send_at, created_at, status, account_id, owner)
|
||||
VALUES (?, ?, ?, ?, '[]', '9999-12-31T00:00:00', ?, 'agent_draft', ?, ?)
|
||||
""",
|
||||
[
|
||||
("draft-ownerless", "nobody@example.com", "Ownerless", "old", "2026-01-01", "acct-a", ""),
|
||||
("draft-bob", "bob@example.com", "Bob", "bob body", "2026-01-02", "acct-b", "bob"),
|
||||
],
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
router = email_routes.setup_email_routes()
|
||||
list_pending = _route_endpoint(router, "/api/email/pending", "GET")
|
||||
approve_pending = _route_endpoint(router, "/api/email/pending/{sid}/approve", "POST")
|
||||
cancel_pending = _route_endpoint(router, "/api/email/pending/{sid}", "DELETE")
|
||||
|
||||
alice_rows = await list_pending(owner="alice")
|
||||
bob_rows = await list_pending(owner="bob")
|
||||
|
||||
assert alice_rows["pending"] == []
|
||||
assert [row["id"] for row in bob_rows["pending"]] == ["draft-bob"]
|
||||
assert (await approve_pending("draft-ownerless", owner="alice"))["success"] is False
|
||||
assert (await cancel_pending("draft-ownerless", owner="bob"))["success"] is False
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"SELECT id, status FROM scheduled_emails ORDER BY id",
|
||||
).fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
assert rows == [("draft-bob", "agent_draft"), ("draft-ownerless", "agent_draft")]
|
||||
|
||||
|
||||
def test_scheduled_poller_resolves_config_with_row_owner(tmp_path, monkeypatch):
|
||||
import routes.email_helpers as email_helpers
|
||||
import routes.email_pollers as email_pollers
|
||||
|
||||
Reference in New Issue
Block a user