Relationships
#1961 Extension version binaries never persisted — binaries security notice has never rendered
Opened by keeb · 9/2/2026
Description
binaries is accepted on publish, validated, and attached to the in-memory version record — then silently dropped before the insert. It has never been written to the database. The consequence is that the extension page's binaries security notice has never rendered for any extension on the registry.
Found while triaging #1951 (unrelated README/changelog bug); filing separately since it shares no code with that fix.
Where it breaks
MongoExtensionRepository.addVersion builds the document field by field and has no binaries key:
lib/infrastructure/mongo-extension-repository.ts:1402-1422— theExtensionVersionDocumentliteral.contentMetadata,releaseNotesanddiffeach get a conditional spread;binariesis absent entirely.
Everything on both sides of that line is correct, which is what makes it invisible:
routes/api/v1/extensions/confirm.ts:142— accepted off the wire asbinaries: body.binarieslib/app/confirm-push.ts:268—const binaries = validateBinaries(req.binaries);lib/app/confirm-push.ts:324— set onversionDatalib/infrastructure/mongo-extension-repository.ts:98— declared on the document type (binaries?: string[])lib/infrastructure/mongo-extension-repository.ts:157— mapped on read (binaries: doc.binaries ?? [])
So the write path type-checks, the read path type-checks, and the ?? [] at the read makes the missing field indistinguishable from an extension that genuinely ships no binaries.
Verification
Queried prod Mongo (Atlas, test db) directly:
db.extension_versions.find({ binaries: { $exists: true } }) → 0 documentsNot "0 non-empty" — the field is absent from every version document in the registry. Nothing has ever been written.
Impact
A security disclosure never renders.
routes/extensions/[...name].tsx:761-778is the "Security Notice" panel that warns "This extension includes executable binaries that run with your user permissions. Inspect before use." and lists the binary names. It is gated onhasBinaries([...name].tsx:572,(latestVersion?.binaries.length ?? 0) > 0), which is permanentlyfalse. An extension that ships executable binaries is presented to installers identically to one that does not.Note the sibling
hasExecutableScriptswarning in the same panel derives fromcontentMetadata.skills, which is persisted — so that half still works, and the panel keeps appearing for skill-bearing extensions. That's part of why this went unnoticed: the section is not obviously dead.Version diffs can never report binary changes.
lib/app/confirm-push.ts:293feedspreviousVersion.binaries ?? []intocomputeVersionDiff, which always reads[]. Adding or removing a binary produces no diff category, so it never appears in the auto-generated changelog.
Steps to reproduce
- Publish an extension whose manifest declares a
binariesentry. - Visit its page on swamp.club — no binary names are listed and no binaries security notice appears.
- Confirm in Mongo: the
extension_versionsdocument for that version has nobinariesfield.
When it broke
Dead since the feature shipped — 754cedac, "extensions: accept and display binaries field from push metadata (#303) (#519)". The commit added the wire format, the validation, the document type, the read mapping and the renderer, but not the write. Not a regression; it has never worked.
Related but distinct: #589 (extension push error for disallowed file types doesn't mention binaries field, shipped) is about the push-time error message, not persistence.
Suggested fix
Add binaries to the inserted document in addVersion. Prefer an unconditional binaries: version.binaries ?? [] over a truthiness spread — the read already normalizes with ?? [], and a conditional spread is what let this hide in the first place. Worth a round-trip test (write → read → rendered flag) rather than a document-shape assertion, since a shape assertion on the literal would have passed against the broken code.
Existing published versions cannot be backfilled from the request (the data was never captured), but binaries is re-derivable from the archives in S3 if a repair pass is wanted; otherwise it heals on next publish.
Triaged
Click a lifecycle step above to view its details.
Sign in to post a ripple.