Skip to content

v0.25.0 — opt-in self-update for git-clone installs (security-reviewed)

Choose a tag to compare

@kalinbogatzevski kalinbogatzevski released this 15 Jul 08:09

Opt-in self-update for git-clone installs

Marketplace installs already self-update via Claude Code. A local git clone install didn't — it sat on old code until someone ran git pull. Now:

export CAPTAIN_MEMO_AUTO_UPDATE=1

On session start, Captain fast-forwards your checkout to the newest stable vX.Y.Z tag on its own origin, runs bun install, restarts the worker, and shows a ⚓ Captain Memo auto-updated: vX → vY banner — then verifies the worker actually booted and rolls back if it didn't. Off by default (auto-pulling a developer's checkout should be a deliberate choice). No-op on a marketplace / non-git install. Throttled to one git fetch per 6h (CAPTAIN_MEMO_AUTO_UPDATE_INTERVAL_MS).

The git mechanics are a moat-safe re-lift of the battle-tested federation self-updater.

This was security-reviewed before release — two serious holes never shipped

The feature went through a 4-lens adversarial review with per-finding verification. It caught two real defects:

RCE via git argument-injection (fixed)

The current branch name (git rev-parse --abbrev-ref HEAD) flowed unsanitized into git fetch … origin <branch>. A repo whose HEAD is a branch named --upload-pack=… executed arbitrary code on an opted-in user's next session — reproduced end-to-end on git 2.47.3, and it fired before the clean-tree / ff-only gates. Fixed two ways: the branch positional is dropped from the fetch entirely, and any dash-leading ref is refused. Re-verified against a real dash-named-HEAD repo — it now bails before any git command runs.

Origin invariant not actually enforced (fixed)

Candidate tags came from git tag --list — the entire local tag namespace — so a contributor who added a fork remote could get a malicious v99.0.0 fast-forwarded in, despite the "only from your own origin" promise. Now candidates are scoped to git ls-remote --tags origin, and git fetch --tags --force guarantees a name in that set resolves to origin's exact sha.

Also hardened from the same review

  • Rollback if the new code crash-loops (capture prior HEAD sha → reset + reinstall + restart old code).
  • Repo-identity gate — only touch a checkout whose package.json name is captain-memo, so a marketplace install nested in an unrelated git repo can't mis-target it.
  • Concurrency lock — only one session updates at a time.
  • bun install gets its own 300s budget instead of the 20s git fetch cap (a half-written node_modules would crash the worker).
  • GIT_TERMINAL_PROMPT=0 + ssh BatchMode — an auth prompt can't stall session start.
  • Suppressed a redundant second worker restart.

20 unit tests (fake git port, no real git) cover every gate and both exploits; the git path was also dry-run and the RCE reproduction re-run against the real repo. Suite: 1119/1119.