I was part of the team behind latest Rsync release, using Proof to perform the full continuous audit. And I wanted to share how it feels to be behind popular non commercial OSS project, and what kind of pressure and effort it takes to maintain it.
There is also an official case study page, if you onto it.
By our internal count, a few hundred security reports and candidate findings passed through the rsync 3.5.0 cycle. Out of them Proof formally filed 99 findings; 28 were later withdrawn. The release shipped with 33 security fixes. These are three different numbers, and the distance between them is where most of the work happened.
I have spent about ten years working in open source, so I thought I understood pressure on a mature project: the backlog that never reaches zero, behaviour nobody wants to touch, users depending on things that were never properly documented, and a very small number of people who understand why the code has the shape it does.
rsync still changed my sense of scale.
Thirty years of promises
From the outside, rsync is a command that moves files. Inside, it is thirty years of operating systems, filesystems, protocols, security assumptions and compatibility decisions compressed into one codebase.
Every flag is a promise to somebody. An option added in 2004 may still be part of a backup system that has run quietly for twenty years. A strange exit code may decide whether a production job retries. A path-handling rule may look inconsistent until you find the old platform or deployment that made it necessary.
People often use “legacy” as a synonym for bad engineering. I used to see old code that way more than I do now. Some of it is debt, obviously, but some of it is simply the price of remaining useful for thirty years. Most software disappears before it has to carry this much history.
The repository does not contain the whole project. You can read every line and still miss why a behaviour exists, which compatibility promise it protects, or which apparently cleaner implementation was already tried and rejected. A great deal of that knowledge lives with Andrew Tridgell, and other project maintainers. I do not think rsync could be maintained safely today without Andrew, or someone with an equivalent amount of project memory, and there is no quick way to manufacture such a person. rsync has been evolving from his original 1996 release ever since.
This is a different kind of engineering from building the first clean version. Keeping software alive while the world changes around it depends as much on judgment and memory as on implementation. At exactly the point where that human context has become most valuable, generating work for the people who hold it has become almost free.
There is always another report
Maintaining a project like rsync now means knowing that somewhere in the queue there may be a serious vulnerability—and that it may look almost identical to the last ten reports that went nowhere.
A report arrives describing remote code execution, path traversal or an authentication bypass. It contains real function names and enough technical detail to sound convincing. It may have taken five minutes to generate. Deciding whether it is real can take hours.
You have to reproduce the configuration, understand who controls the input, inspect the surrounding code, and often recover the history behind the behaviour. Sometimes the report is nonsense. Sometimes it is wrong but points towards a different problem. Sometimes it is real. You cannot know without doing the work.
By our internal count, a few hundred candidate reports and potential findings passed through the rsync 3.5.0 cycle. They came from our audit, fuzzing, Trail of Bits, independent researchers, and people running models against the repository. They were not a few hundred vulnerabilities. They were a few hundred claims that experienced people had to understand well enough to classify.
This lands on top of everything else: reviewing patches, supporting old branches, fixing regressions, preparing releases and answering users. Each report pulls the maintainer into another part of a codebase where a careless decision may affect backup systems all over the world.
The asymmetry is difficult to ignore. Reports can be generated much faster than a project can investigate them. There is almost no cost to being wrong, while the cost of checking the claim falls on the people with the deepest context and the least replaceable time.
Then LLM use inside our own process became another reason to attack the project. On Discord and elsewhere, months of triage, reproductions, withdrawn findings, patches and tests were reduced to: you used LLMs, screw you, we are done with rsync.
I found that infuriating. We were not publishing raw model output as security research. The models produced claims and made mistakes. Maintainers and contributors still had to reproduce them, reject them, correct the requirements, review the fixes and take responsibility for the release.
We have made it almost free to create more work for open-source maintainers. We have not made judgment, historical knowledge or responsibility any cheaper.
And there is always another report.
Deciding what was real
Most of the difficult work began after a report looked plausible. Who controls the input? Which process owns the path? Does the behaviour require a non-default configuration? Is it documented? Would changing it break a workflow that has existed for fifteen years? Is it a vulnerability, a robustness problem, or behaviour that looks dangerous only when removed from its history?
One of our requirements appeared almost embarrassingly simple:
A host matching
hosts denymust not be admitted.
The check failed. When a hostname in a deny rule could not be resolved, rsync skipped the rule. Under the affected configuration, the control failed open and could admit the host it was intended to block. That became CVE-2026-70452, rated HIGH.
We also found that transferred filenames could place raw control characters into rsync logs. A filename entered as data, but when an administrator later opened the log in a terminal, those bytes could become terminal escape sequences. rsync now escapes them.
Both problems look obvious after they have a name, a patch and a regression test. Before that, they sit among thousands of unusual behaviours, many of which exist for legitimate reasons.
Proof formally filed 99 findings and later withdrew 28 of them. We kept the withdrawals visible because an audit has to be capable of discovering that it was wrong. Large finding counts create pressure to preserve claims after the evidence has weakened, but a claim had to survive a working reproduction, a coherent threat model, maintainer review and the history of the code. Sometimes it did not.
My role grew beyond filing reports. I joined the rsync admins group and worked on triage, tests, pull-request review, and the arguments around where a security issue ends and expected behaviour begins.
I will not pretend I was casual about seeing it there. rsync was already part of the engineering landscape when I started in open source. Being trusted inside the release mattered to me, and it also changed the feeling of the work. Once a patch lands, you think about the path you did not test. When a requirement changes, you wonder who depended on the previous behaviour.
Proof helped because it gave that uncertainty somewhere to live. A model-generated claim did not become the conclusion. It entered a requirements graph connected to code, evidence and a concrete reproduction. Maintainers and contributors could challenge the assumption, correct the threat model, or show that the supposed boundary had never existed. When our understanding changed, the graph changed with it.
For each concrete requirement, it built an executable tripwire against the real rsync source tree. While a defect still reproduced, the tripwire remained live. When a fix landed, it stopped reproducing, and the accepted behaviour moved into an upstream regression test. The models still made mistakes; the important part was that those mistakes had a visible place to be corrected, and the corrected requirement could run against the next version.
Trail of Bits worked on the same release through its Patch the Planet program. They found issues we did not find, and we found issues they did not find, including some that had survived in rsync for decades and, in ten cases, since the first release in 1996. A companion fuzzing pass and other researchers found more. The 33 fixes in rsync 3.5.0 came from that combined work, not from one tool or one team.
What the project kept
The line in the rsync 3.5.0 release notes that matters most to me is:
“Every fix ships with a regression test in the test suite that fails on the unfixed tree.”
A report can disappear into an archive, and an argument about a threat model becomes difficult to reconstruct. Even Andrew cannot be expected to remember every detail forever. A test keeps part of that reasoning inside the project.
Most audits end with a document whose accuracy begins decaying as soon as the code changes. This one kept running throughout the development of 3.5.0. When upstream changed, we merged the changes, reran the tripwires, retired fixed findings, added new ones, and corrected the record.
rsync 3.5.0 shipped on August 13. The reports did not stop, and neither did the audit. The tripwires remain in place and will run against the next release candidate. But our long term plans is to configure real-time continuous audit, watch for the updates.
The project now retains more of what we learned instead of asking the same few people to remember all of it again.
If you are non-commercial open-source project, reach us at Proof and we can help you with your backlog, issue triage, and ease maintainer pains.
If you are a commercial project looking to move your software quality scale in pace with modern agentic flows you are even more welcome :)
Huge kudos to all the OSS maintainers making our day to day life possible. Keep this people safe.

