← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-42462 · CWE-347 · Disclosed 2026-05-20

Fedify Linked Data Signature bypass via JSON-LD named-graph restructuring

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

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.

"Signature bypass is real, but it lives in a niche federation path with high attacker friction and no host-level blast radius"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Establish a federation foothold

The attacker needs a way to participate in ActivityPub flows, typically by operating a federated peer or otherwise receiving signed activities from another server. The practical weapon is just a normal ActivityPub-compatible server plus custom relay logic; no public exploit kit was found. This already narrows the population from 'any internet attacker' to someone working inside a niche protocol ecosystem.
Conditions required:
  • Target uses @fedify/fedify in 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
Where this breaks in practice:
  • 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
Detection/coverage: Internet scanners and dependency tools can find the package version, but perimeter scanners cannot reliably identify exploitable Fedify usage from banners alone.
STEP 02

Reshape the signed JSON-LD payload

Using a custom JSON-LD rewriter based on the GHSA details, the attacker restructures a valid signed document with @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.
Conditions required:
  • 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
Where this breaks in practice:
  • No public turnkey PoC located during review
  • Exploitability depends on how the target app consumes signed activity types in practice
Detection/coverage: Web/app logs can sometimes catch suspicious @graph, @included, or @reverse keywords, but only if request bodies are retained or inspected after decompression.
STEP 03

Pass signature verification on the target

The vulnerable Fedify path verifies the signature against the canonical graph, then the application interprets the mutated document shape differently than intended. In the advisory's examples, this can turn a signed 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.
Conditions required:
  • Target runs a vulnerable Fedify version
  • Application has not added its own post-compaction keyword rejection and strict local-context compaction
Where this breaks in practice:
  • 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
Detection/coverage: SCA/SBOM tools should flag vulnerable versions; runtime detection is weak unless the app logs canonicalized vs parsed payload differences.
STEP 04

Land integrity impact in the app layer

Impact is primarily integrity and secondarily availability: forged or replayed activities, stripped metadata, altered objects, or changed actor attributes such as routing details. This is bad for trust in a federated social workflow, but it is still app-layer abuse rather than code execution on the host. The blast radius usually stays inside the affected Fedify-based service and its federation interactions.
Conditions required:
  • The application takes meaningful action on the manipulated activity
  • Business logic trusts Fedify's verification result
Where this breaks in practice:
  • No direct privilege escalation to OS, container, or cloud control plane
  • Per-instance blast radius is bounded by the specific app's federation logic
Detection/coverage: Application anomaly detection may notice unusual activity transitions, replay patterns, or mismatched actor/object relationships, but generic EDR will not.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo public evidence of active exploitation found. I found no CISA KEV entry and no campaign reporting tied to this CVE.
Proof-of-concept availabilityNo standalone public PoC repo located. The GitHub advisory itself is detailed enough to hand-build an exploit, which keeps attacker friction above commodity level.
EPSSNo 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 statusNot listed in CISA KEV as checked against the public catalog on 2026-05-31.
CVSS vectorCVSS: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 versionsMainline: 2.2.3. Backports visible in vendor changelog: 2.1.14, 2.0.18, 1.10.10, 1.9.11.
Scanning / exposure realityPoor 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 timelineGHSA published: 2026-05-20. Fedify fixes released: 2026-05-21. GitHub Advisory DB / GLAD publication observed: 2026-05-26.
Reporter / sourcePublic advisory was published in the Fedify repository by dahlia. I did not find an independent researcher write-up beyond the vendor/GHSA material.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.8/10)

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.

HIGH Affected versions and fixed releases
MEDIUM Real-world exploitability in typical enterprise environments
MEDIUM Absence of public PoC and active exploitation reporting

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.

05 · Compensating Control

What to do — in priority order.

  1. Reject dangerous JSON-LD keywords after compaction — Add server-side validation to block @graph, @included, and @reverse after 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.
  2. 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.
  3. 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.
  4. 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.
What doesn't work
  • 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.
06 · Verification

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.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/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()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, do not treat this like a fire-drill unless you actually run internet-facing Fedify-based federation services. First inventory where @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

  1. GitHub Security Advisory GHSA-9rfg-v8g9-9367
  2. GitHub Advisory Database entry
  3. Fedify changelog
  4. Fedify 2.2.3 release
  5. GitLab Advisory Database entry
  6. CISA Known Exploited Vulnerabilities Catalog
  7. Fedify project homepage
  8. CraftedSignal public threat summary
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.