This is a wax seal on the envelope while the message gets rearranged after delivery
CVE-2026-42462 affects the @fedify/fedify ActivityPub framework for TypeScript. The flaw is in Linked Data Signature verification: a remote actor can reshape a signed JSON-LD activity with features such as @graph, @included, @reverse, or alias tricks so the RDF graph still verifies while the application interprets different semantics. GitHub and GitLab advisory data describe affected versions as < 2.2.3, while the Fedify changelog shows coordinated backport fixes released on May 21, 2026 for 1.9.11, 1.10.10, 2.0.18, 2.1.14, and 2.2.3.
Technically this is serious because it breaks a trust boundary: verified signed federation traffic can be replayed, stripped, or reinterpreted. Operationally, though, this is not an enterprise-searing emergency. It requires a vulnerable Fedify-based internet-facing federated app, a reachable inbox path, and an attacker who can obtain or relay exploitable signed ActivityPub content; there is no evidence of broad internet exploitation, no KEV listing, no commodity PoC, and no direct path to server takeover.
4 steps from start to impact.
Establish a federation foothold
- Target uses
@fedify/fedifyin a server that accepts remote ActivityPub traffic - Federation endpoints are reachable from the internet
- Attacker can obtain signed third-party activities to replay or transform
- Fedify is a library, not a mass-deployed appliance, so exposed population is inherently smaller
- The attacker must understand ActivityPub and JSON-LD edge cases, not just send commodity exploit traffic
Reshape the signed JSON-LD payload
@graph, @included, @reverse, or alias remapping so the canonical RDF graph stays signature-valid. The advisory specifically calls out moving a top-level Activity into @graph, promoting the embedded object, or hiding fields outside the normal tree. This is the core bypass step and the reason the vendor CVSS uses AC:H.- Target processes Linked Data Signatures on incoming activities
- Original signed content contains shapes that become dangerous when reinterpreted
- Attacker can craft syntactically valid JSON-LD
- No public turnkey PoC located during review
- Exploitability depends on how the target app consumes signed activity types in practice
@graph, @included, or @reverse keywords, but only if request bodies are retained or inspected after decompression.Pass signature verification on the target
Undo{Announce}-style structure into something the receiver processes as a top-level object, or strip attributes from Create and Update activities. The attack works because verification and interpretation disagree.- Target runs a vulnerable Fedify version
- Application has not added its own post-compaction keyword rejection and strict local-context compaction
- Well-instrumented applications may reject malformed or unusual ActivityPub payloads before business logic
- Some real-world payload types may not yield useful impact even if the signature bypass works
Land integrity impact in the app layer
- The application takes meaningful action on the manipulated activity
- Business logic trusts Fedify's verification result
- No direct privilege escalation to OS, container, or cloud control plane
- Per-instance blast radius is bounded by the specific app's federation logic
The supporting signals.
| In-the-wild status | No public evidence of active exploitation found. I found no CISA KEV entry and no campaign reporting tied to this CVE. |
|---|---|
| Proof-of-concept availability | No standalone public PoC repo located. The GitHub advisory itself is detailed enough to hand-build an exploit, which keeps attacker friction above commodity level. |
| EPSS | No browsable EPSS value surfaced during review. FIRST's public API exists, but I did not find a published score page for this CVE in indexed sources, so treat EPSS as *unknown* rather than low. |
| KEV status | Not listed in CISA KEV as checked against the public catalog on 2026-05-31. |
| CVSS vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L from GitHub/GitLab. That matches the technical shape: remote and authless, but high-complexity with integrity-heavy impact. |
| Affected versions | @fedify/fedify before 2.2.3 per GitHub/GitLab advisory data; the maintained branches shown by the vendor changelog were all affected before their May 21, 2026 fixes. |
| Fixed versions | Mainline: 2.2.3. Backports visible in vendor changelog: 2.1.14, 2.0.18, 1.10.10, 1.9.11. |
| Scanning / exposure reality | Poor internet-wide measurability. Fedify is a library/framework, not a fingerprintable product banner, so Shodan/Censys/FOFA-style counts are not a reliable denominator. Your practical exposure comes from SBOMs, lockfiles, containers, and source repos. |
| Disclosure timeline | GHSA published: 2026-05-20. Fedify fixes released: 2026-05-21. GitHub Advisory DB / GLAD publication observed: 2026-05-26. |
| Reporter / source | Public advisory was published in the Fedify repository by dahlia. I did not find an independent researcher write-up beyond the vendor/GHSA material. |
noisgate verdict.
The single biggest downward pressure is reachable population: this is a niche library flaw in Fedify-backed ActivityPub services, not a broadly exposed enterprise platform. Even where deployed, the attacker still needs to work through a high-complexity federation and JSON-LD transformation chain to land application-level integrity abuse rather than host compromise.
Why this verdict
- Start point: technically serious trust-boundary break — signature verification can succeed while the app interprets a different activity, so the flaw deserves more than backlog hygiene.
- Downward adjustment: attacker position is narrower than CVSS suggests — practical exploitation usually implies a federated peer or access to third-party signed ActivityPub traffic, which is not a common attacker starting point in enterprise networks.
- Downward adjustment: exposed population is small and hard to mass-scan — Fedify is a developer library with low measurable internet footprint, so there is no evidence this is a broad fleet-scale internet fire.
Why not higher?
There is no KEV listing, no public exploitation evidence, and no commodity exploit path. More importantly, this is not pre-auth RCE on a popular appliance; it is a high-complexity, protocol-specific integrity flaw in a smaller framework ecosystem, with blast radius mostly confined to the consuming application's federation logic.
Why not lower?
Calling this LOW would ignore the core issue: verified signed content can be reinterpreted or stripped across a trust boundary. If you run internet-facing Fedify services, an attacker does not need local access or credentials on the target, and the resulting business impact can include forged actions and corrupted federation state.
What to do — in priority order.
- Reject dangerous JSON-LD keywords after compaction — Add server-side validation to block
@graph,@included, and@reverseafter compacting to the application's local context, matching the advisory guidance. Because this is a MEDIUM finding there is no mitigation SLA; use this as an interim hardening step while you schedule patching inside the 365-day remediation window. - Log and alert on suspicious ActivityPub bodies — Capture inbound federation request bodies or normalized field extracts and alert on the presence of those JSON-LD keywords plus unusual root/object inversions. There is no mitigation SLA for MEDIUM, but this is worth deploying now on any internet-facing fediverse service because version-only detection is otherwise weak.
- Inventory Fedify in code and containers — Search lockfiles, SBOMs, node_modules layers, and container images for
@fedify/fedify, then map each finding to the fixed branch versions. There is no mitigation SLA here; do it as the first step toward completing remediation within the 365-day window. - Constrain federation where business permits — If a given app does not need broad federation, reduce who can post into its inbox or gate inbound peers through allowlists and proxy controls. This does not replace patching, but it lowers the number of hostile remote actors who can exercise the vulnerable path while you move through the MEDIUM remediation window.
- EDR on the host: this is application-layer content manipulation, not a malware dropper or local privilege escalation.
- MFA: the attack does not depend on interactive user authentication to the target service.
- Network perimeter signatureing alone: unless you inspect JSON request bodies and account for compression/format variation, generic IDS rules will miss the semantic trick.
Crowdsourced verification payload.
Run this on a developer workstation, CI runner, container image, or target host filesystem that contains the application source or installed node_modules. Invoke it as python3 check_fedify_cve_2026_42462.py /path/to/app with read access only; no admin privileges are required.
#!/usr/bin/env python3
# check_fedify_cve_2026_42462.py
# Detects vulnerable @fedify/fedify versions for CVE-2026-42462.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/runtime error
import json
import os
import re
import sys
from pathlib import Path
PKG = "@fedify/fedify"
FIXED = {
(1, 9): (1, 9, 11),
(1, 10): (1, 10, 10),
(2, 0): (2, 0, 18),
(2, 1): (2, 1, 14),
(2, 2): (2, 2, 3),
}
SEMVER_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$")
def parse_semver(v):
if not v:
return None
v = v.strip()
m = SEMVER_RE.match(v)
if not m:
return None
return tuple(int(x) for x in m.groups())
def cmp_ver(a, b):
return (a > b) - (a < b)
def status_for(version_str):
ver = parse_semver(version_str)
if ver is None:
return "UNKNOWN", f"Unparseable version: {version_str}"
major_minor = (ver[0], ver[1])
if major_minor in FIXED:
fixed = FIXED[major_minor]
if cmp_ver(ver, fixed) >= 0:
return "PATCHED", f"{version_str} >= fixed {'.'.join(map(str, fixed))}"
return "VULNERABLE", f"{version_str} < fixed {'.'.join(map(str, fixed))}"
# Vendor/GHSA advisory describes affected versions as < 2.2.3.
# For branches without explicit backport data, conservatively treat anything older as vulnerable.
if cmp_ver(ver, (2, 2, 3)) < 0:
return "VULNERABLE", f"{version_str} falls below advisory mainline fix 2.2.3 and has no explicit branch exemption"
return "PATCHED", f"{version_str} is newer than 2.2.3"
def find_package_json_versions(root):
findings = []
for path in root.rglob("package.json"):
try:
data = json.loads(path.read_text(encoding="utf-8"))
except Exception:
continue
if isinstance(data, dict):
if data.get("name") == PKG and data.get("version"):
findings.append((str(path), data.get("version"), "installed-package"))
for section in ("dependencies", "devDependencies", "optionalDependencies", "peerDependencies"):
deps = data.get(section, {})
if isinstance(deps, dict) and PKG in deps:
findings.append((str(path), str(deps[PKG]), f"declared-{section}"))
return findings
def normalize_declared_range(s):
# Best-effort extraction of a concrete version from simple ranges like ^2.2.2, ~2.1.13, 2.0.18.
m = re.search(r"(\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?)", s)
return m.group(1) if m else None
def main():
if len(sys.argv) != 2:
print("UNKNOWN - usage: python3 check_fedify_cve_2026_42462.py /path/to/app")
sys.exit(3)
root = Path(sys.argv[1])
if not root.exists():
print(f"UNKNOWN - path does not exist: {root}")
sys.exit(3)
findings = find_package_json_versions(root)
if not findings:
print("UNKNOWN - @fedify/fedify not found in package.json files under target path")
sys.exit(2)
vuln = []
patched = []
unknown = []
for file_path, raw_version, source in findings:
version = raw_version
if source.startswith("declared-"):
extracted = normalize_declared_range(raw_version)
if extracted is None:
unknown.append((file_path, raw_version, source, "Range cannot be resolved offline"))
continue
version = extracted
state, reason = status_for(version)
record = (file_path, raw_version, source, reason)
if state == "VULNERABLE":
vuln.append(record)
elif state == "PATCHED":
patched.append(record)
else:
unknown.append(record)
if vuln:
print("VULNERABLE")
for f, v, s, r in vuln:
print(f"- {f} [{s}] => {v} :: {r}")
if patched:
print("Additional patched findings:")
for f, v, s, r in patched:
print(f"- {f} [{s}] => {v} :: {r}")
if unknown:
print("Additional unknown findings:")
for f, v, s, r in unknown:
print(f"- {f} [{s}] => {v} :: {r}")
sys.exit(1)
if patched and not unknown:
print("PATCHED")
for f, v, s, r in patched:
print(f"- {f} [{s}] => {v} :: {r}")
sys.exit(0)
print("UNKNOWN")
for f, v, s, r in unknown:
print(f"- {f} [{s}] => {v} :: {r}")
if patched:
print("Additional patched findings:")
for f, v, s, r in patched:
print(f"- {f} [{s}] => {v} :: {r}")
sys.exit(2)
if __name__ == "__main__":
main()
If you remember one thing.
@fedify/fedify exists in code, images, and lockfiles; for exposed fediverse apps, add the JSON-LD keyword blocks and logging now as interim hardening if you can, but for a MEDIUM finding there is no noisgate mitigation SLA — go straight to the 365-day remediation window. Use the noisgate remediation SLA to finish upgrades to a fixed branch version within 365 days, with internet-facing federation workloads handled earlier in your normal application-risk queue rather than buried in backlog forever.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.