Reports reach Lemmy's moderators, from an anonymous reporter
Lemmy takes a report only from a person or a service, about one post or comment, addressed to its community, and it answered PrivaPub's Flag (the instance actor's, an Application, with no `to` and the account and posts as its object) 400. A report of a post or comment in a community on a server whose NodeInfo names Lemmy now leaves from `privapub_reports`, a Service with its own key that names nobody: one Flag per post, `to` the community (its own audience, else its thread's), with the persona's words, or the category, in `summary` and `content`, sent to the community's inbox. This is the second exception to "a server's software is for display" (owner decision 2026-10-06, `ReportService.ServiceReportTakers`). Every other server keeps the instance actor's report. An account alone is not reported to Lemmy, which takes no such report, and `forwarded` now says whether anything left. The reporter is read unsigned in SecureMode and answers WebFinger like the instance actor. Nobody follows or mentions it, the Mastodon API has no account for it, and a migration reserves its name. Checked live: Lemmy 1.0 and 0.19 keep the reports of a thread and of a comment, with alice's words, from "Reports from privapub.test", and none names her (69 checks). A sweep of every scenario with this and the next commit: 876 checks pass; Ghost's Network feed listed alice's post too late once, and Ghost passes alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
10ff4b3d2a
commit
f66c280b0b
33 files changed
+561
-46
No files matched your search
@@ -115,6 +115,37 @@ namespace PrivaPub.Tests.Http
|
||||
Assert.Equal(HttpStatusCode.NotFound, browser.Status);
|
||||
}
|
||||
|
||||
// Lemmy takes a report only from a Person, a Service or an Organization, whose document names its preferredUsername,
|
||||
// inbox, outbox and key; a browser gets the document too, as from the instance actor: the reporter has no page
|
||||
[Fact]
|
||||
public async Task The_reporter_is_a_service_with_an_inbox_an_outbox_and_its_own_key()
|
||||
{
|
||||
var fetched = await _client.Fetch("/peasants/privapub_reports");
|
||||
var browser = await _client.Fetch("/peasants/privapub_reports", Browser);
|
||||
var outbox = await _client.Fetch("/peasants/privapub_reports/anus");
|
||||
var instance = await _client.Fetch("/peasants/privapub");
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, fetched.Status);
|
||||
var actor = fetched.Json;
|
||||
var id = $"{Base}/peasants/privapub_reports";
|
||||
Assert.Equal(id, actor["id"]!.GetValue<string>());
|
||||
Assert.Equal("Service", actor["type"]!.GetValue<string>());
|
||||
Assert.Equal("privapub_reports", actor["preferredUsername"]!.GetValue<string>());
|
||||
Assert.Equal(id + "/mouth", actor["inbox"]!.GetValue<string>());
|
||||
Assert.Equal(id + "/anus", actor["outbox"]!.GetValue<string>());
|
||||
Assert.Equal(id, actor["url"]!.GetValue<string>());
|
||||
Assert.Equal(id + "#main-key", actor["publicKey"]!["id"]!.GetValue<string>());
|
||||
Assert.Equal(id, actor["publicKey"]!["owner"]!.GetValue<string>());
|
||||
Assert.NotEqual(instance.Json["publicKey"]!["publicKeyPem"]!.GetValue<string>(), actor["publicKey"]!["publicKeyPem"]!.GetValue<string>());
|
||||
Assert.False(actor["discoverable"]!.GetValue<bool>());
|
||||
Assert.Null(actor["wall"]);
|
||||
Assert.Null(actor["implements"]);
|
||||
Assert.Equal(HttpStatusCode.OK, browser.Status);
|
||||
Assert.Equal("Service", browser.Json["type"]!.GetValue<string>());
|
||||
Assert.Equal(HttpStatusCode.OK, outbox.Status);
|
||||
Assert.Equal(0, outbox.Json["totalItems"]!.GetValue<int>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Users_moves_permanently_to_peasants_and_unknown_actors_are_404()
|
||||
{
|
||||
@@ -507,7 +538,7 @@ namespace PrivaPub.Tests.Http
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Secure_mode_refuses_unsigned_gets_except_for_the_instance_actor()
|
||||
public async Task Secure_mode_refuses_unsigned_gets_except_for_the_servers_own_actors()
|
||||
{
|
||||
var secure = await SecureModeHost.Shared();
|
||||
using var client = secure.Client();
|
||||
@@ -533,6 +564,9 @@ namespace PrivaPub.Tests.Http
|
||||
var instance = await client.Fetch("/peasants/privapub");
|
||||
Assert.Equal(HttpStatusCode.OK, instance.Status);
|
||||
Assert.Equal("Application", instance.Json["type"]!.GetValue<string>());
|
||||
var reporter = await client.Fetch("/peasants/privapub_reports");
|
||||
Assert.Equal(HttpStatusCode.OK, reporter.Status);
|
||||
Assert.Equal("Service", reporter.Json["type"]!.GetValue<string>());
|
||||
Assert.Equal(HttpStatusCode.OK, (await client.Fetch("/")).Status);
|
||||
foreach (var path in paths[..^1])
|
||||
Assert.True((await client.Fetch(reader.SignedGet(path))).Status == HttpStatusCode.OK, $"{path} refused a signed GET");
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace PrivaPub.Tests.Http
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Lookup_finds_local_and_remote_accounts_but_never_a_circle_or_the_instance_actor()
|
||||
public async Task Lookup_finds_local_and_remote_accounts_but_never_a_circle_or_the_servers_own_actors()
|
||||
{
|
||||
var alice = await _host.Mastodon("alice");
|
||||
var (bob, bobId) = await Remote();
|
||||
@@ -149,6 +149,7 @@ namespace PrivaPub.Tests.Http
|
||||
Assert.Equal(HttpStatusCode.NotFound, (await anonymous.Get($"/api/v1/accounts/lookup?acct={circleName}")).Status);
|
||||
Assert.Equal(HttpStatusCode.NotFound, (await alice.Client.Get($"/api/v1/accounts/lookup?acct={circleName}")).Status);
|
||||
Assert.Equal(HttpStatusCode.NotFound, (await anonymous.Get("/api/v1/accounts/lookup?acct=privapub")).Status);
|
||||
Assert.Equal(HttpStatusCode.NotFound, (await anonymous.Get("/api/v1/accounts/lookup?acct=privapub_reports")).Status);
|
||||
Assert.Equal(HttpStatusCode.NotFound, (await anonymous.Get($"/api/v1/accounts/lookup?acct=nobody{Guid.NewGuid():N}")).Status);
|
||||
Assert.Equal(HttpStatusCode.NotFound, (await anonymous.Get("/api/v1/accounts/lookup?acct=a@b@c")).Status);
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace PrivaPub.Tests.Http
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WebFinger_finds_the_instance_actor_and_a_community()
|
||||
public async Task WebFinger_finds_the_servers_own_actors_and_a_community()
|
||||
{
|
||||
var owner = await _host.Persona(await _host.SignUp(), "fingerowner");
|
||||
var community = await _host.FederatedGroup(owner, community: true);
|
||||
@@ -120,6 +120,10 @@ namespace PrivaPub.Tests.Http
|
||||
Assert.Equal($"acct:privapub@{Domain}", instance.Json["subject"]!.GetValue<string>());
|
||||
Assert.Equal($"{Base}/peasants/privapub", Link(instance.Json, "self"));
|
||||
Assert.Single(instance.Json["links"]!.AsArray());
|
||||
var reporter = await WebFinger($"acct:privapub_reports@{Domain}");
|
||||
Assert.Equal(HttpStatusCode.OK, reporter.Status);
|
||||
Assert.Equal($"{Base}/peasants/privapub_reports", Link(reporter.Json, "self"));
|
||||
Assert.Single(reporter.Json["links"]!.AsArray());
|
||||
Assert.Equal(HttpStatusCode.OK, group.Status);
|
||||
Assert.Equal($"acct:{community.UserName}@{Domain}", group.Json["subject"]!.GetValue<string>());
|
||||
Assert.Equal(community.Uri, Link(group.Json, "self"));
|
||||
|
||||
Reference in new issue
Block a user