June 19, 2026
How I Found CVE-2026–50131: An Incomplete SSRF Fix in Fedify
There is a very specific feeling you get when you are reading security code and something looks almost right.
Chaitanya Garware
5 min read
Not obviously broken.
Not screaming "critical vulnerability."
Just slightly incomplete.
That is how CVE-2026–50131 started for me.
I was reviewing Fedify, a TypeScript library used for building federated server applications powered by ActivityPub. Since ActivityPub software often works by fetching remote objects, activities, documents, and media from URLs, URL validation becomes a very important security boundary. If that validation is weak, a feature that is supposed to fetch a public remote resource can accidentally become a path toward Server-Side Request Forgery, also known as SSRF.
Fedify had already addressed an earlier SSRF/internal network access issue in GHSA-p9cg-vqcc-grcx. The fix added public URL validation before outbound runtime fetching. On paper, that is exactly the kind of defense you want.
But while looking at the post-fix validation logic, I noticed something interesting.
The protection depended on a function named validatePublicUrl(), which eventually relied on IPv4 classification logic to decide whether a resolved destination was public or not. The function blocked the most familiar unsafe ranges:
10.0.0.0/8
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
At first glance, that looks reasonable. These are the ranges most developers immediately think of when they hear "private IP" or "localhost." But SSRF defense is not only about blocking the obvious private ranges.
That was the gap.
The Bug Was Not That There Was No Protection
The important thing about this finding is that Fedify did have protection.
This was not a case where the application blindly fetched anything.
The issue was more subtle: the protection was based on an incomplete manual denylist.
That distinction matters because incomplete security fixes are a common source of real vulnerabilities. A previous bug gets patched, the obvious cases are covered, tests pass, and everyone moves on. But the patch itself becomes the new security boundary. If that boundary is too narrow, attackers do not need the old bug anymore. They only need to find what the new fix forgot.
In this case, the validator correctly rejected some private and local IPv4 destinations, but it still treated several special-use ranges as valid public destinations.
Examples included:
100.64.0.0/10 for carrier-grade NAT
198.18.0.0/15 for benchmarking and internal testing networks
224.0.0.0/4 for multicast
240.0.0.0/4 for reserved addresses
192.0.0.0/24 for IETF protocol assignments
Documentation ranges such as:
192.0.2.0/24
198.51.100.0/24
203.0.113.0/24
These should not be treated the same as globally routable public internet destinations in a security-sensitive URL validation function.
Reproducing the Behavior
To confirm the issue, I isolated the IPv4 validation behavior and tested it directly.
The core logic was simple: split the IPv4 address into parts, parse the first and second octets, reject a few known ranges, and return true for everything else.
That "everything else" was the problem.
When I tested common private ranges like 127.0.0.1, 10.0.0.1, 192.168.1.1, and 169.254.169.254, the function rejected them correctly.
But when I tested special-use ranges like 100.64.0.1, 198.18.0.1, 224.0.0.1, and 240.0.0.1, the validator returned true.
That meant the function responsible for deciding whether a URL destination was public could approve addresses that were not truly public internet destinations.
For an SSRF mitigation, that is a serious correctness issue.
Why This Was Not Just a Duplicate
One thing I wanted to be careful about was classification.
Fedify already had a previous SSRF-related advisory. So I did not want to simply re-report the same issue in a different form.
The difference was that the earlier issue involved missing validation before fetching remote ActivityPub resources. My report focused on the validation that was added afterward.
In other words, this was about the fix itself.
The original mitigation introduced a new protection layer, but the IPv4 classification inside that protection was incomplete. So the report was best understood as an incomplete fix or bypass class for the previous SSRF mitigation, rather than a duplicate of the original bug.
This is one of the lessons I keep seeing in vulnerability research:
A patch is code too.
And security patches deserve the same level of adversarial review as the original vulnerable feature.
Security Impact
Any Fedify feature that accepts or processes remote ActivityPub object, activity, document, or media URLs and relies on validatePublicUrl() as an SSRF protection boundary could incorrectly allow outbound requests to special-use IPv4 destinations.
The real-world impact depends on the deployment environment, network routing, and how Fedify is integrated by an application. I did not attempt to access any real internal service. The proof focused only on the validation decision itself: the function accepted destinations that should not have been considered public.
That is also why responsible disclosure matters. The goal is not to prove damage. The goal is to identify a security boundary that can fail before someone else abuses it.
The Fix
The safer approach is to avoid relying on a small manual denylist for public IP validation.
Instead of asking, "Is this address not in the few private ranges I remembered?" the code should ask, "Is this address actually globally routable and safe to treat as public?"
That difference is huge.
A stronger fix should reject all relevant special-use IPv4 ranges, including private, loopback, link-local, carrier-grade NAT, benchmarking, multicast, reserved, documentation, and other non-public ranges.
Even better, security-sensitive public IP validation should use a maintained IP classification library or a carefully reviewed allowlist-style approach based on globally routable addresses.
Manual network denylisting is easy to get wrong because IPv4 has many special-purpose blocks that are not obvious unless you are specifically looking for them.
Disclosure and Publication
I reported the issue privately through GitHub Security Advisories.
The advisory was published as GHSA-xw9q-2mv6–9fr8 and assigned CVE-2026–50131.
The vulnerability was rated High severity with a CVSS score of 8.6.
Patched versions were released for the affected Fedify lines, including:
1.9.12
1.10.11
2.0.19
2.1.15
2.2.4
Seeing the CVE record published was a special moment for me, not because of the number itself, but because this finding represents the kind of security research I enjoy most: reading real code, understanding the intended security model, and finding the small mismatch between what the code is supposed to protect and what it actually protects.
What I Learned
This finding reinforced a few things for me.
First, SSRF defenses are harder than they look. Blocking localhost and private IPs is not enough. A correct SSRF defense needs strong URL parsing, DNS resolution handling, redirect handling, IP classification, and protection against edge cases.
Second, incomplete fixes are a valuable research area. Many researchers look for brand-new bug classes, but reviewing previous patches can be just as powerful. If a patch introduced a new validation layer, that layer becomes worth testing.
Third, security research is often about patience. The bug was not hidden behind a complex exploit chain. It was sitting in logic that looked reasonable at first glance. The key was slowing down and asking whether "public IP" was being defined completely enough for a security boundary.
And finally, good vulnerability reports should be careful. I tried to keep the proof focused, avoid touching any real internal service, explain why this was not a duplicate, and give maintainers a clear path to fix the issue.
That is the kind of research I want to keep doing.
Not just finding bugs for the sake of finding bugs, but helping make real open-source software safer.
CVE-2026–50131 is now public, and I am grateful to the Fedify maintainers and GitHub Security Advisory process for handling the report.