apache / apache/ponymail-foal

Security: can_access_list() grants any authoritative user access to ALL private lists (missing per-list authorization)

Open
#330 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
45
Forks
23
PR merge metrics
No merged PRs in 30d

Description

server/plugins/aaa.py:can_access_list() ignores the requested list id and grants access to every private list to any session whose OAuth login is authoritative. There is no per-list membership / subscriber / moderator / owner check anywhere in the backend, so a user who is authorized for one private list (or for none) can read the archives of all private lists on the instance.

This was reported privately to security@apache.org first, the PMC confirmed it is a fix-before-release item (no CVE, as Foal is unreleased) and asked me to raise it here for public discussion / a PR. The analysis is entirely source-based against master.

Affected: apache/ponymail-foal master (git-deployed; unreleased). Verified at 88f227b1d2.

Root cause:
```python
# server/plugins/aaa.py
def can_access_email(session, email) -> bool:
if not email.get("private", True): # public -> always
return True
return can_access_list(session, email.get("list_raw", None))

def can_access_list(session, _listid) -> bool:
# If logged in via a known oauth, we assume access for now...TO BE CHANGED
if session.credentials and session.credentials.authoritative:
return True
return False
```

_listid is never used. The developer comment (...TO BE CHANGED) already flags this as a placeholder. authoritative is set for anyone whose OAuth provider is listed in oauth.authoritative_domains (server/plugins/session.py):

creds["authoritative"] = internal.get("oauth_provider") in server.config.oauth.authoritative_domains

Reachability:
server/plugins/messages.py:get_email() gates returned documents on can_access_email() -> can_access_list(), and /api/email, /api/mbox, /api/thread, /api/stats all reach it. An authoritative-domain user requesting a private list's messages receives them.

Impact:
Cross-list disclosure of private archives. Severity scales with oauth.authoritative_domains:

- default is [] (nobody has private access — not exposed out of the box);
- a single-org OAuth provider -> any org member can read all private lists (security@, board@, PMC-private, …);
- a broad public provider (e.g. a generic Google/GitHub domain) -> effectively any account holder can read all private archives.

There is currently no configuration that limits a user to their own private lists. Access is all-or-nothing per authoritative domain.

Thanks!

Contributor guide

No contributing guide indexed for this repository

Research direction

Read server/plugins/aaa.py first, then trace authoritative credential setup in server/plugins/session.py and document retrieval through server/plugins/messages.py. Follow the /api/email, /api/mbox, /api/thread, and /api/stats paths to understand the affected checks. Done means private-list access is verified per requested list rather than granted to every authoritative session.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
authorization, backend, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.