fix(security): notify-only update check + interactive upgrade flow#21
Merged
lxcong merged 3 commits intochainbase-labs:mainfrom May 1, 2026
Merged
Conversation
- check-mcp.sh: replace shell-interpolated $HOME inside python3 -c with
os.path.expanduser('~/.claude.json') to eliminate a latent shell-injection
vector when $HOME contains quote characters. Also use typed except.
- SECURITY.md: add a Security Posture section documenting what the skill
reads/writes, network egress, credential handling, and supply chain.
Add a Scanner false-positive notes section explaining why VirusTotal
and ClawScan may flag check-update.sh (self-update) and check-mcp.sh
(credential read) as Suspicious, and why both patterns are intentional.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace self-applying git fetch+checkout with a notify-only flow modeled after gstack's update-check (https://github.com/garrytan/gstack): - check-update.sh now only emits UP_TO_DATE | UPGRADE_AVAILABLE <old> <new> | (silent). Never invokes git. Never modifies the install. The user runs `npx skills update chainbase-labs/agentkey` themselves. - Two-tier cache TTL (60 min for UP_TO_DATE, 12 h for UPGRADE_AVAILABLE) so we detect new releases quickly but don't hammer the GitHub API once an upgrade is known. - Validate remote response with a regex so HTML error pages or rate-limit JSON don't get cached as a fake version. - Cache invalidates automatically when local version moves past the cached "old" — handles the manually-updated user case. - SKILL.md Step 0 routing now handles UPGRADE_AVAILABLE and tells the user the manual update command. UPDATED / UPDATE_FAILED handlers removed (script no longer emits those). - SECURITY.md updated to reflect the new posture: no self-modification, no git, single GET to api.github.com, exit. Scanner false-positive note rewritten accordingly — the "remote-controlled binary update" framing no longer fits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pt-in Build the second tier of gstack's update UX on top of the notify-only check-update.sh. The script remains scanner-clean (no git, no execve); the agent provides the interaction. Persistent state under ~/.config/agentkey/: - auto-upgrade — touch file: skip the prompt and apply updates silently - update-snoozed — "<version> <level> <epoch>" with 24h/48h/7d backoff - update-disabled — touch file: silence update checks entirely check-update.sh changes: - Exit silently if update-disabled exists - Read update-snoozed; suppress UPGRADE_AVAILABLE while within the current level's window. New remote version invalidates the snooze. - Cache layer unchanged; snooze gates output, not caching. SKILL.md Step 0 changes: - On UPGRADE_AVAILABLE, branch to a new "Upgrade flow" subsection. - Step A: skip prompt if AGENTKEY_AUTO_UPGRADE=1 or ~/.config/agentkey/auto-upgrade exists; auto-apply via Step C. - Step B: AskUserQuestion with Yes / Always / Not now / Never. Each option has its own side effect (snooze write, disable touch, auto- upgrade touch) before continuing. - Step C: invoke `npx skills update chainbase-labs/agentkey`. SECURITY.md additions: - Document the three new config files and which side reads/writes each - Update the scanner false-positive note: check-update.sh remains pure-notify; the upgrade execution lives in the agent's interactive layer, gated by explicit user consent or a previously persisted opt-in flag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
@claude review |
lxcong
added a commit
that referenced
this pull request
May 1, 2026
🤖 I have created a release *beep* *boop* --- ## [1.2.1](v1.2.0...v1.2.1) (2026-05-01) ### Bug Fixes * **security:** notify-only update check + interactive upgrade flow ([#21](#21)) ([a05efd5](a05efd5)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardens the AgentKey skill's security posture so ClawHub's
Suspiciousflags can be cleared, and rebuilds the update UX into a two-tier design modeled after gstack:check-update.sh): scanner-safe, notify-only detection. SingleGET https://api.github.com/..., printsUP_TO_DATE/UPGRADE_AVAILABLE <old> <new>/ silent. Nogit, noexecve, no in-place writes outside${TMPDIR}cache.SKILL.mdStep 0): interactive upgrade flow. Agent surfacesAskUserQuestionwith Yes / Always / Not now / Never and runs the actual update vianpx skills updateonly after explicit consent (or a previously persisted opt-in).What's in the PR
1.
check-update.sh— notify-only with snooze + disableReplaces the previous self-applying
git fetch+git checkoutwith a pure detection script. New behavior:GETtoapi.github.com, validates the response with a regex, compares against localversion.txt.UP_TO_DATE, 12 h forUPGRADE_AVAILABLE).~/.config/agentkey/update-disabled(silent if present).~/.config/agentkey/update-snoozed(escalating 24h / 48h / 7d backoff per<version> <level> <epoch>format; new remote version invalidates the snooze).<old>(handles the user-updated-manually case).2.
SKILL.mdStep 0 — interactive upgrade flowWhen
check-update.shoutputsUPGRADE_AVAILABLE <old> <new>, the agent now:AGENTKEY_AUTO_UPGRADE=1env var or~/.config/agentkey/auto-upgradefile. If either is set: announce "Auto-upgrading…" and run the upgrade silently.AskUserQuestion:npx skills update chainbase-labs/agentkey~/.config/agentkey/auto-upgrade, then upgrade~/.config/agentkey/update-snoozedwith escalating level~/.config/agentkey/update-disabled3.
check-mcp.sh— shell-injection hardeningReplace shell-interpolated
'$HOME/.claude.json'inside thepython3 -cblock withos.path.expanduser('~/.claude.json'). Removes a latent injection vector when$HOMEcontains quote characters. Also tightenexcept:→except Exception:.4.
SECURITY.md— Security Posture + scanner notes~/.config/agentkey/and which side reads/writes eachapi.github.comfrom the script;npxto npm registry only via user-consented agent action)check-update.sh(notify-only GitHub call) andcheck-mcp.sh(credential-pattern read for status check) may match heuristics, and why both patterns are intentional and boundedThreat-model framing
Scanners scanning the repo source see:
check-update.sh— pure detection. One outbound GET. Nogit, noexecve. No "remote-controlled binary update" pattern any more.check-mcp.sh— local read of two known config paths to verify the API key is configured. Output is a status code; key value discarded. No exfiltration path.SKILL.md— text instructions for the agent (not executable code) describing the interactive flow.The actual
npx skills updateinvocation lives in the agent's runtime, gated by either explicit user consent (AskUserQuestion) or a previously persisted opt-in flag the user set themselves. This is the same threat model as gstack and any other Claude Code skill that performs interactive operations.What is NOT in this PR (and why)
@agentkey/mcp@^1in manual-install JSON — drafted and reverted.^1only blocks major-version supply-chain attacks; minor/patch attacks within1.xstill go through, and the doc-snippet pin doesn't reach the--auth-loginwriter (lives in the server repo). Cost (manual installers must re-edit JSON for patches) outweighed the marginal protection.Test plan
bash -npasses on both shell scriptscheck-update.shend-to-end tested:UP_TO_DATEwhen versions matchUPGRADE_AVAILABLE 0.9.0 1.2.0when local lagsupdate-disabledpresent → silent regardlessos.path.expanduserblock runs cleanly against an existing~/.claude.jsonclawhub skill publishso the new SECURITY.md and scripts ship in the published bundle, thenclawhub skill rescan agentkey🤖 Generated with Claude Code