Skip to main content
← Back to list
01Issue
BugShippedSwamp Club
Assigneesstack72

Relationships

#737 Yank semantics inconsistent: all-versions-yanked acts as a free hidden/private extension; extension-level yank hard-blocks re-push

Opened by keeb · 6/21/2026· Shipped 6/22/2026

Description

There are two yank modes with inconsistent push behavior, and one of them is a no-pay backdoor to a hidden extension listing.

Mode A — extension-level yank (extensions.yankedAt set)

initiatePush hard-blocks any new push: if (existing.yankedAt) throw new YankedError(...) (lib/app/push-extension.ts). The publisher cannot republish; only an admin unyankExtension recovers it.

Mode B — all versions yanked (yankVersion per version)

yankVersion deliberately leaves extensions.yankedAt null (comment: "keeps the name reusable for a subsequent push"). When every version is yanked, the denormalized latestVersion becomes null. Consequences:

  • The extension disappears from the registry list — the default browse filter publishedVersionFilter() requires latestVersion != null (lib/infrastructure/mongo-extension-repository.ts).
  • Its detail page renders "ALL VERSIONS YANKED" via allVersionsYanked = !isYanked && !extension.latestVersion (routes/extensions/[...name].tsx).
  • initiatePush ALLOWS pushing (since yankedAt is null).

Net effect: yanking all versions yields a hidden-but-mutable extension — effectively a private extension for free, bypassing the paid private-extension entitlement.

Inconsistency / question to resolve

  • Mode A blocks re-push; Mode B allows it. They should converge.
  • Desired behavior (per product): publishing a NEW version of a yanked extension should UNYANK it (clear yankedAt and restore visibility) rather than throw. Confirm and implement.
  • Decide whether "all versions yanked" should be a reachable state for non-admins at all, given it duplicates the (paid) private-extension feature.

Steps to reproduce (Mode B / backdoor)

  1. Publish an extension with a single stable version.
  2. Yank that version (yankVersion). latestVersion → null.
  3. Observe the extension is gone from /extensions and shows "ALL VERSIONS YANKED", yet extensions.yankedAt is null and a further push is accepted.

Relevant files

  • lib/app/push-extension.ts (initiatePush yank guard)
  • lib/app/yank-extension.ts (yankExtension / yankVersion / unyankExtension / unyankVersion)
  • lib/infrastructure/mongo-extension-repository.ts (publishedVersionFilter)
  • routes/extensions/[...name].tsx (allVersionsYanked label)

Environment

swamp-club registry, prod.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 3 MOREPR_MERGED+ 1 MORENOTIFICATION_SKIPPED

Shipped

6/22/2026, 9:38:04 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack726/22/2026, 9:13:33 PM

Sign in to post a ripple.