Skip to content

Commit 777a6c4

Browse files
committed
Merge tag '1.8.5'
Fedify 1.8.5
2 parents 6690c81 + e158983 commit 777a6c4

File tree

4 files changed

+201
-14
lines changed

4 files changed

+201
-14
lines changed

CHANGES.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,29 @@ Version 1.9.0
99
To be released.
1010

1111

12+
Version 1.8.5
13+
-------------
14+
15+
Released on August 8, 2025.
16+
17+
### @fedify/fedify
18+
19+
- Fixed a critical authentication bypass vulnerability in the inbox handler
20+
that allowed unauthenticated attackers to impersonate any ActivityPub actor.
21+
The vulnerability occurred because activities were processed before
22+
verifying that the HTTP Signatures key belonged to the claimed actor.
23+
Now authentication verification is performed before activity processing to
24+
prevent actor impersonation attacks. [[CVE-2025-54888]]
25+
26+
### @fedify/cli
27+
28+
- Fixed `fedify nodeinfo` color support in Windows Terminal.
29+
[[#358], [#360] by KeunHyeong Park]
30+
31+
[#358]: https://github.com/fedify-dev/fedify/issues/358
32+
[#360]: https://github.com/fedify-dev/fedify/pull/360
33+
34+
1235
Version 1.8.4
1336
-------------
1437

@@ -273,6 +296,19 @@ the versioning.
273296
[iTerm]: https://iterm2.com/
274297

275298

299+
Version 1.7.9
300+
-------------
301+
302+
Released on August 8, 2025.
303+
304+
- Fixed a critical authentication bypass vulnerability in the inbox handler
305+
that allowed unauthenticated attackers to impersonate any ActivityPub actor.
306+
The vulnerability occurred because activities were processed before
307+
verifying that the HTTP Signatures key belonged to the claimed actor.
308+
Now authentication verification is performed before activity processing to
309+
prevent actor impersonation attacks. [[CVE-2025-54888]]
310+
311+
276312
Version 1.7.8
277313
-------------
278314

@@ -398,6 +434,19 @@ Released on June 25, 2025.
398434
[#252]: https://github.com/fedify-dev/fedify/pull/252
399435

400436

437+
Version 1.6.8
438+
-------------
439+
440+
Released on August 8, 2025.
441+
442+
- Fixed a critical authentication bypass vulnerability in the inbox handler
443+
that allowed unauthenticated attackers to impersonate any ActivityPub actor.
444+
The vulnerability occurred because activities were processed before
445+
verifying that the HTTP Signatures key belonged to the claimed actor.
446+
Now authentication verification is performed before activity processing to
447+
prevent actor impersonation attacks. [[CVE-2025-54888]]
448+
449+
401450
Version 1.6.7
402451
-------------
403452

@@ -526,6 +575,19 @@ the versioning.
526575
[#242]: https://github.com/fedify-dev/fedify/pull/242
527576

528577

578+
Version 1.5.5
579+
-------------
580+
581+
Released on August 8, 2025.
582+
583+
- Fixed a critical authentication bypass vulnerability in the inbox handler
584+
that allowed unauthenticated attackers to impersonate any ActivityPub actor.
585+
The vulnerability occurred because activities were processed before
586+
verifying that the HTTP Signatures key belonged to the claimed actor.
587+
Now authentication verification is performed before activity processing to
588+
prevent actor impersonation attacks. [[CVE-2025-54888]]
589+
590+
529591
Version 1.5.4
530592
-------------
531593

@@ -700,6 +762,19 @@ Released on March 28, 2025.
700762
[multibase]: https://github.com/multiformats/js-multibase
701763

702764

765+
Version 1.4.13
766+
--------------
767+
768+
Released on August 8, 2025.
769+
770+
- Fixed a critical authentication bypass vulnerability in the inbox handler
771+
that allowed unauthenticated attackers to impersonate any ActivityPub actor.
772+
The vulnerability occurred because activities were processed before
773+
verifying that the HTTP Signatures key belonged to the claimed actor.
774+
Now authentication verification is performed before activity processing to
775+
prevent actor impersonation attacks. [[CVE-2025-54888]]
776+
777+
703778
Version 1.4.12
704779
--------------
705780

@@ -949,6 +1024,21 @@ Released on February 5, 2025.
9491024
[#195]: https://github.com/fedify-dev/fedify/issues/195
9501025

9511026

1027+
Version 1.3.20
1028+
--------------
1029+
1030+
Released on August 8, 2025.
1031+
1032+
- Fixed a critical authentication bypass vulnerability in the inbox handler
1033+
that allowed unauthenticated attackers to impersonate any ActivityPub actor.
1034+
The vulnerability occurred because activities were processed before
1035+
verifying that the HTTP Signatures key belonged to the claimed actor.
1036+
Now authentication verification is performed before activity processing to
1037+
prevent actor impersonation attacks. [[CVE-2025-54888]]
1038+
1039+
[CVE-2025-54888]: https://github.com/fedify-dev/fedify/security/advisories/GHSA-6jcc-xgcr-q3h4
1040+
1041+
9521042
Version 1.3.19
9531043
--------------
9541044

packages/cli/src/nodeinfo.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,14 @@ function checkTerminalColorSupport(): "truecolor" | "256color" | "none" {
299299
return "256color";
300300
}
301301

302+
// Check for Windows Terminal support
303+
// FIXME: WT_SESSION is not a reliable way to check for Windows Terminal support
304+
const isWindows = Deno.build.os === "windows";
305+
const isWT = Deno.env.get("WT_SESSION");
306+
if (isWindows && isWT != null && isWT !== "") {
307+
return "truecolor";
308+
}
309+
302310
return "none";
303311
}
304312

packages/fedify/src/federation/handler.test.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
respondWithObject,
4141
respondWithObjectIfAcceptable,
4242
} from "./handler.ts";
43+
import { InboxListenerSet } from "./inbox.ts";
4344
import { MemoryKvStore } from "./kv.ts";
4445
import { createFederation } from "./middleware.ts";
4546

@@ -1386,6 +1387,94 @@ test("respondWithObject()", async () => {
13861387
});
13871388
});
13881389

1390+
test("handleInbox() - authentication bypass vulnerability", async () => {
1391+
// This test reproduces the authentication bypass vulnerability where
1392+
// activities are processed before verifying the signing key belongs
1393+
// to the claimed actor
1394+
1395+
const federation = createFederation<void>({ kv: new MemoryKvStore() });
1396+
let processedActivity: Create | undefined;
1397+
const inboxListeners = new InboxListenerSet<void>();
1398+
inboxListeners.add(Create, (_ctx, activity) => {
1399+
// Track that the malicious activity was processed
1400+
processedActivity = activity;
1401+
});
1402+
1403+
// Create malicious activity claiming to be from victim actor
1404+
const maliciousActivity = new Create({
1405+
id: new URL("https://attacker.example.com/activities/malicious"),
1406+
actor: new URL("https://victim.example.com/users/alice"), // Impersonating victim
1407+
object: new Note({
1408+
id: new URL("https://attacker.example.com/notes/forged"),
1409+
attribution: new URL("https://victim.example.com/users/alice"),
1410+
content: "This is a forged message from the victim!",
1411+
}),
1412+
});
1413+
1414+
// Sign request with attacker's key (not victim's key)
1415+
const maliciousRequest = await signRequest(
1416+
new Request("https://example.com/", {
1417+
method: "POST",
1418+
body: JSON.stringify(await maliciousActivity.toJsonLd()),
1419+
}),
1420+
rsaPrivateKey3, // Attacker's private key
1421+
rsaPublicKey3.id!, // Attacker's public key ID
1422+
);
1423+
1424+
const maliciousContext = createRequestContext({
1425+
request: maliciousRequest,
1426+
url: new URL(maliciousRequest.url),
1427+
data: undefined,
1428+
documentLoader: mockDocumentLoader,
1429+
federation,
1430+
});
1431+
1432+
const actorDispatcher: ActorDispatcher<void> = (_ctx, identifier) => {
1433+
if (identifier !== "someone") return null;
1434+
return new Person({ name: "Someone" });
1435+
};
1436+
1437+
const response = await handleInbox(maliciousRequest, {
1438+
recipient: "someone",
1439+
context: maliciousContext,
1440+
inboxContextFactory(_activity) {
1441+
return createInboxContext({
1442+
url: new URL(maliciousRequest.url),
1443+
data: undefined,
1444+
documentLoader: mockDocumentLoader,
1445+
federation,
1446+
recipient: "someone",
1447+
});
1448+
},
1449+
kv: new MemoryKvStore(),
1450+
kvPrefixes: {
1451+
activityIdempotence: ["_fedify", "activityIdempotence"],
1452+
publicKey: ["_fedify", "publicKey"],
1453+
},
1454+
actorDispatcher,
1455+
inboxListeners,
1456+
onNotFound: () => new Response("Not found", { status: 404 }),
1457+
signatureTimeWindow: { minutes: 5 },
1458+
skipSignatureVerification: false,
1459+
});
1460+
1461+
// The vulnerability: Even though the response is 401 (unauthorized),
1462+
// the malicious activity was already processed by routeActivity()
1463+
assertEquals(response.status, 401);
1464+
assertEquals(await response.text(), "The signer and the actor do not match.");
1465+
1466+
assertEquals(
1467+
processedActivity,
1468+
undefined,
1469+
`SECURITY VULNERABILITY: Malicious activity with mismatched signature was processed! ` +
1470+
`Activity ID: ${processedActivity?.id?.href}, ` +
1471+
`Claimed actor: ${processedActivity?.actorId?.href}`,
1472+
);
1473+
1474+
// If we reach here, the vulnerability is fixed - activities with mismatched
1475+
// signatures are properly rejected before processing
1476+
});
1477+
13891478
test("respondWithObjectIfAcceptable", async () => {
13901479
let request = new Request("https://example.com/", {
13911480
headers: { Accept: "application/activity+json" },

packages/fedify/src/federation/handler.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -787,20 +787,6 @@ async function handleInboxInternal<TContextData>(
787787
span.setAttribute("activitypub.activity.id", activity.id.href);
788788
}
789789
span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
790-
const routeResult = await routeActivity({
791-
context: ctx,
792-
json,
793-
activity,
794-
recipient,
795-
inboxListeners,
796-
inboxContextFactory,
797-
inboxErrorHandler,
798-
kv,
799-
kvPrefixes,
800-
queue,
801-
span,
802-
tracerProvider,
803-
});
804790
if (
805791
httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)
806792
) {
@@ -823,6 +809,20 @@ async function handleInboxInternal<TContextData>(
823809
headers: { "Content-Type": "text/plain; charset=utf-8" },
824810
});
825811
}
812+
const routeResult = await routeActivity({
813+
context: ctx,
814+
json,
815+
activity,
816+
recipient,
817+
inboxListeners,
818+
inboxContextFactory,
819+
inboxErrorHandler,
820+
kv,
821+
kvPrefixes,
822+
queue,
823+
span,
824+
tracerProvider,
825+
});
826826
if (routeResult === "alreadyProcessed") {
827827
return new Response(
828828
`Activity <${activity.id}> has already been processed.`,

0 commit comments

Comments
 (0)