What this is
In April 2026 our founder reviewed Nora, a single binary artifact registry written in Rust by a solo developer, as part of the Review Bomb series on his personal blog. The full review, including the parts about what the project does well, is published at wshoffner.dev.
We are reproducing the findings here because the shape of that review is the shape of a code audit as we run one. Three problems came out of a single pass through the codebase, they sat at three clearly different severities, and each one needed a different kind of argument to establish. Nora is not our project and its maintainer is not our client. What makes the review useful as evidence is that all three findings were submitted upstream as pull requests and all three were merged by the person who owns the code, on April 5 and April 6, 2026.
For context on the codebase: roughly 19,600 lines of Rust across 45 source files, 411 test functions, property-based tests, fuzz targets, and a CI pipeline running formatting, clippy with warnings denied, cargo-audit, cargo-deny, Trivy, Gitleaks, CodeQL, and OpenSSF Scorecard. This is a well-tended project, and the findings below are the kind that survive in well-tended projects precisely because everything visible is already working.
Finding one: two registries invisible to operations
Nora serves seven package protocols, and it labels each incoming request with the registry that handled it so that Prometheus can count them and the health endpoint can report on them. The function that does the labeling, detect_registry(), had match arms for Docker, Maven, npm, PyPI, and Cargo, while Go module and raw file requests fell through to an "other" catch-all. Every request to those two registries was therefore uncountable in the metrics, indistinguishable from anything else the catch-all swept up.
The same gap appeared in the health path. The RegistriesHealth struct carried five fields for five registries, so the Go and Raw registries had nowhere to report from, and the health endpoint described them as down while they were serving traffic normally.
Neither symptom is visible from inside the application. The registries worked, the requests succeeded, and the artifacts landed where they were supposed to. What broke was the operator's picture of the system, in the two places an operator would look first when something went wrong. That is the severity tier we would call moderate in a client report: no data at risk, no failing request, and an incident response that would start from a false premise.
The fix added the missing match arms, added the two fields to the health struct and to its construction in check_registries_health(), and went out as PR #97. It was the first external pull request the project had ever received. The maintainer reviewed, approved, and merged it the same day, with a comment that is worth quoting because it is the evidence that the finding was welcome rather than merely tolerated:
"This is the very first community PR that NORA has ever received, and it means a lot. The fact that you not only noticed the missing Go and Raw registries in metrics, but took the time to write a clean fix with proper tests... Welcome to the team."
Finding two: the test that was holding the bug in place
The reason the first finding had survived in a project with 411 tests is the second finding, and it is the one we point at most often when clients ask why an audit reads test code as carefully as it reads production code.
There was already a test for Go registry path detection. It asserted that a Go module request should be labeled "other", and it passed, because that is exactly what the code did. The test had encoded the bug as the expected behavior, so every subsequent run of the suite confirmed the defect and reported the confirmation as a pass.
A test suite in that condition is worse than an absent one, because an absent test leaves an obvious gap while a wrong test actively defends the thing it should be catching. Any later contributor who fixed detect_registry() correctly would have watched CI go red and had every reason to assume they were the ones who had broken something.
Correcting the assertion was part of the same pull request. The point for an audit reader is the ordering: the test had to be read as a claim about intended behavior and checked against the rest of the system, rather than treated as a passing check and skipped over.
Finding three: the latent data loss in the garbage collector
The third finding was the one worth the review on its own, and it was latent rather than live, which is the category that ordinary usage will never surface.
Nora's garbage collector works the way most do. One function, collect_all_blobs, walks storage and gathers every blob it can see. Another, collect_referenced_digests, gathers every blob that something still points at. Whatever appears in the first set and not the second is an orphan, and running nora gc without --dry-run deletes it.
The collector scanned all seven registry prefixes for keys containing /blobs/ or /tarballs/. The reference resolver read Docker manifests, and only Docker manifests. Every non-Docker artifact in storage was therefore collected as a candidate and could never be marked as referenced, which made it an orphan by construction. npm tarballs match the /tarballs/ filter directly, so a real garbage collection run against a registry hosting npm packages would have deleted live, referenced packages.
Nothing about this is visible in normal operation. The garbage collector had tests and they passed, because they exercised the Docker path where the two halves of the algorithm agree. The bug needed a specific and entirely reasonable set of conditions to fire: a registry hosting more than Docker images, and an operator who trusts the tool enough to run it for real rather than in dry-run mode. That combination had not happened yet, and it was going to.
The remediation in PR #109 narrowed blob collection to the docker/ prefix and dropped the /tarballs/ match, so the two halves of the algorithm cover the same ground until per-registry reference resolution exists for the other six protocols. Reducing the collector's reach is the conservative choice; it leaves some genuine Docker orphans uncollected in exchange for making deletion of live data structurally impossible. Three new tests went with it, including one that runs a real garbage collection pass and asserts that a Docker orphan is deleted while an npm tarball sitting in the same storage survives.
A third pull request, #108, came out of the same reading. The S3Credentials struct carried a #[zeroize(skip)] annotation on secret_access_key while its adjacent comment explained that access_key_id was the field that did not need scrubbing. In practice the secret was still being zeroed on drop, because the wrapper type has its own #[zeroize(drop)], so nothing was leaking. The annotation simply contradicted its own comment and would have started leaking silently the day anyone changed the wrapper's drop behavior. That one is a low severity finding, and we report those the same way we report the others: with the reasoning about why it is not currently exploitable stated as plainly as the reasoning about why it should still be fixed.
Why the severity tiers matter
Reading these three findings together says something about what a report is for that a flat list of issues cannot.
The metrics gap needed fixing because operators make decisions from those numbers, but nothing was going to be lost while it waited. The garbage collector needed fixing on a different timescale entirely, because the cost of leaving it was somebody's packages, and the trigger condition was ordinary use of a supported feature. The zeroize annotation needed fixing eventually, and needed an accompanying explanation of why it was not urgent so that nobody spent an afternoon on it ahead of the collector.
A report that presents all three as issues, or that inflates the low one to make the list look weightier, forces the reader to redo the triage we were engaged to do. Getting the tiers right is most of the value, and the tiers only hold up if the reasoning behind each one is written down where the maintainer can disagree with it.
In this case the maintainer did not disagree with any of it, and all three findings landed within two days.
Checking any of this
The review is published in full at wshoffner.dev, and the three pull requests (#97, #108, #109) are public, including their diffs, their test plans, and the maintainer's own comments on each.
If you want this kind of reading applied to a codebase you are responsible for, the services page explains how an engagement is scoped.