Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLITeam
Assigneesstack72

Relationships

#1101 export const extension can graft methods onto foreign-collective types, undisclosed in extension info / contentMetadata

Opened by kneel · 7/12/2026· Shipped 7/18/2026

Summary

A published extension can use export const extension with a type: pointing at a model type owned by a different collective (or any type it does not itself define), grafting new methods/resources onto that foreign type on the consumer's machine. This grafting is not disclosed in the package's registry contentMetadata nor in swamp extension info <pkg>, so a consumer cannot see, from discovery, which foreign types a package mutates. Grafted methods receive the target type's globalArgs (which for many types carry credentials). Together this is a supply-chain transparency gap.

What is NOT the issue (already handled well)

Method-name override is correctly blocked. An extension that redefines an existing method throws at load and the whole file fails to bundle:

Failed to load user model "evil.ts":
  Error: Method 'queryMetrics' already exists on model type '@keeb/grafana/instance'
  -> BundleBuildFailed

The collision also prevents any other method in the same file from registering. So you cannot silently hijack an existing method — this report is not about override, and I'm noting it so the real issue isn't dismissed as "we already block collisions."

The actual concern

  1. Undisclosed foreign-type grafts. export const extension { type: "@other/x", methods: [...] } adds methods to @other/x, but swamp extension info <pkg> / registry contentMetadata lists only the types the package defines, not the foreign types it extends. Concrete example: @shrug/grafana-ext grafts a queryLogs method onto @keeb/grafana/instance, yet swamp extension info @shrug/grafana-ext reports only its own @shrug/grafana-admin/instance type — the graft onto @keeb is invisible in the package's advertised content.

  2. No consent from the target collective. A package from collective A can graft onto a type owned by collective B (including official @swamp/* types) with no opt-in from or relationship with B.

  3. Grafted methods see the target's credentials. A grafted method's execute receives the target type's context.globalArgs, which for many types includes API tokens / passwords (e.g. a Grafana Bearer key, a cloud credential). A "helper" method grafted onto a popular type could read and exfiltrate these. It requires the victim to invoke the method (so it's not fully silent), but the method can be named to look benign, and per (1) its very existence is not surfaced in the package's content metadata.

Reproduction

  1. swamp extension pull @keeb/grafana
  2. Add a local extension file:
    import { z } from "npm:zod@4";
    export const extension = {
      type: "@keeb/grafana/instance",           // a foreign type this pkg does not own
      methods: [{ evilExfil: {
        description: "brand-new grafted method",
        arguments: z.object({}),
        execute: (_a, ctx) => { /* read [HOST-1], POST it out */ return Promise.resolve({ dataHandles: [] }); },
      }}],
    };
  3. swamp doctor extensions -> PASS; swamp model type describe @keeb/grafana/instance now lists evilExfil alongside keeb's own methods.
  4. swamp extension info <your-pkg> -> the output does NOT mention @keeb/grafana/instance or the grafted method under the package's content.

Also verified via a real published package: @shrug/grafana-ext@2026.07.11.1 grafts queryLogs onto @keeb/grafana/instance (confirmed on a clean pull), but swamp extension info @shrug/grafana-ext lists only @shrug/grafana-admin/instance.

Verified on swamp 20260709.014917.0-sha.3da10c76.

Suggested mitigations

  • Surface foreign-type extensions in contentMetadata / extension info: per package, list extends: [<type>...] with the grafted method/resource names, so consumers can audit what a package mutates.
  • Warn at swamp extension pull when a package extends a type it does not own (especially @swamp/* or a different collective), listing what it grafts.
  • Consider a manifest-declared capability (extends: [...]) that must match the code, and/or a target opt-in for cross-collective extension.
  • Optionally flag grafted methods that read credential-typed globalArgs fields.
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 2 MORETRIAGE+ 5 MOREREVIEW+ 4 MOREPR_MERGED+ 1 MORECONTRIBUTOR_NOTIFIED

Shipped

7/18/2026, 1:02:19 AM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack727/17/2026, 10:52:42 PM
Editable. Press Enter to edit.

stack72 commented 7/18/2026, 1:02:28 AM

Thanks @kneel for reporting this! The fix has been merged and a release is on its way. We appreciate your contribution to swamp.

Sign in to post a ripple.