Commit Graph

5 Commits

Author SHA1 Message Date
不白 41e172486a fix(skill): eliminate Hermes scanner findings (#28)
## Summary

Hermes flagged this skill with **3 findings** (1 persistence + 2
traversal) that, combined with `community-source` status, produced a
`BLOCKED` verdict requiring users to install with `--force`. None of the
findings reflected actual risk — all stemmed from incidental code/doc
patterns. This PR removes every match and adds CI to keep them gone.

## Changes

### A — Eliminate path traversal in `check-update.sh`
- Drop `dirname(BASH_SOURCE)/..` resolution and `version.txt` read
- Embed `LOCAL_VERSION` as a constant, synced by release-please via
`extra-files`
- Script now does **zero filesystem traversal** — no `..`, no `dirname`,
no `CLAUDE_PLUGIN_ROOT` dependency

### B — Remove persistent file-path enumeration from `SKILL.md`
- Replace explicit list of agent config paths
(`~/.claude/settings.json`, `claude_desktop_config.json`,
`~/.cursor/mcp.json`) with a neutral one-liner pointing to `SECURITY.md`
- The CLI behavior is fully documented in `SECURITY.md` for transparency
without triggering scanner heuristics

### C — Align `SECURITY.md` with new internals
- Update the `check-update.sh` description to call out the new
zero-traversal design

### Hardening (D1) — script becomes shellcheck-clean
- `set -u` + `set -o pipefail`; explicit `|| true` on every intentional
silent-failure path
- Replace 3× awk fork-and-read on the snooze file with a single atomic
`read -r` (closes a real cross-process race)
- Same treatment for the cache file parse — also eliminates `echo $VAR |
awk` injection surface
- Cache `date +%s` once per run (`NOW`), shared by cache-age and snooze
expiry math
- Switch `LATEST_VERSION` validation from `echo | grep -qE` to `case`
glob, matching how `LOCAL_VERSION` is validated
- **Net**: ~half the forks per run, no silent failures, stricter
validation

### Metadata (D2) — improve community-source traceability
- `SKILL.md` frontmatter: add `author` / `homepage` / `repository` /
`license` fields
- Standard fields, ignored by clients that don't read them, but give
scanners and reviewers a direct path to the publisher

### Release plumbing
- `SKILL.md` frontmatter `version`: 1.0.0 → 1.2.3 (long-standing drift)
and tagged with `# x-release-please-version` so release-please now syncs
it
- `release-please-config.json`: add `check-update.sh` and `SKILL.md` to
`extra-files`
- New workflow `.github/workflows/verify-version-sync.yml` asserts all
four versions match on every relevant push/PR — catches the case where
someone hand-edits one location and forgets the others

`version.txt` remains the **single source of truth** that humans
maintain. The other three locations (`plugin.json`, `SKILL.md`
frontmatter, `check-update.sh`) are auto-synced derivatives.

## Test plan

- [x] Local smoke-test of `check-update.sh` across 11 scenarios:
  - cold run, `UP_TO_DATE` cache hit, `UPGRADE_AVAILABLE` cache hit
  - snoozed (active / expired / long expired)
  - disabled file
  - corrupted binary cache
  - local-version-moved-on
- All paths produce expected output; `set -u` catches no false positives
- [x] `bash -n` syntax check passes
- [x] All four version locations (`version.txt`, `plugin.json`,
`SKILL.md` frontmatter, `check-update.sh`) verified to match at 1.2.3
- [x] `SKILL.md` YAML frontmatter parses correctly with the new fields
- [ ] CI passes
- [ ] Re-scan with Hermes confirms 0 findings (post-merge, after release
tag)

## Notes for reviewer

- All four version locations exist for distinct technical reasons — see
`release-please-config.json` for the sync wiring. Maintainer cost
remains exactly one place (`version.txt`, bumped automatically by
release-please from conventional commits).
- The `set -u` change is the most behavior-affecting — please review the
`|| true` guards on `read -r` and `curl ... | grep | sed` carefully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:21:30 +08:00
不白 bc740c8015 fix(update-check): ship version.txt inside skill so npx-skills-add installs find it (#26)
## Problem

The skill assumed the whole repo is the plugin root (`PLUGIN_ROOT` =
repo root, `version.txt` at repo root). That holds in Claude Code plugin
mode, where Claude Code injects `CLAUDE_PLUGIN_ROOT`. But the README's
recommended path —

```
npx skills add chainbase-labs/agentkey
```

— uses [vercel-labs/skills](https://github.com/vercel-labs/skills),
which only copies the `skills/agentkey/` subdirectory to
`~/.claude/skills/agentkey/`. The repo-root `version.txt` doesn't come
along.

In that layout, `check-update.sh`'s fallback

```bash
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." 2>/dev/null && pwd)}"
```

resolves `../../..` from `~/.claude/skills/agentkey/scripts/` to
`~/.claude/`, so `VERSION_FILE` points at `~/.claude/version.txt` —
which doesn't exist. `LOCAL_VERSION` ends up empty, the script silently
`exit 0`s at line 35, and **skills-CLI users never see upgrade prompts
at all**.

(Worse case: if some other tool ever drops a `~/.claude/version.txt`,
AgentKey would read it as its own version.)

This is an interface contract mismatch between the two distribution
models, not a one-sided bug — both are valid, but the skill needs to
work under either.

## Fix

1. Move `version.txt` into the skill directory
(`skills/agentkey/version.txt`) so it travels with whichever subset of
the repo gets copied.
2. In `check-update.sh`, anchor on `SKILL_ROOT` (one level above
`scripts/`) instead of an external `CLAUDE_PLUGIN_ROOT`. Both
distribution paths now resolve identically:
   - Plugin: `<repo>/skills/agentkey/version.txt`
   - Skills CLI: `~/.claude/skills/agentkey/version.txt`
3. Point release-please at the new path via `version-file`. The
`plugin.json` `extra-files` entry is unchanged.
4. Update docs (README, `docs/README_zh.md`, `.claude/CLAUDE.md`,
`SECURITY.md`, `claude-pr-review.yml`) to reflect the new path.

## Test plan

- [x] `bash skills/agentkey/scripts/check-update.sh` in repo: resolves
`SKILL_ROOT` to `<repo>/skills/agentkey`, reads `LOCAL_VERSION=1.2.2`
correctly.
- [x] Simulated skills-CLI install: `cp -r skills/agentkey /tmp/sim/`
then ran `bash /tmp/sim/agentkey/scripts/check-update.sh` with
`CLAUDE_PLUGIN_ROOT` unset → resolves `SKILL_ROOT=/tmp/sim/agentkey`,
reads `LOCAL_VERSION=1.2.2` correctly.
- [ ] After merge, the next release-please Release PR should bump
`skills/agentkey/version.txt` (along with `plugin.json` and
`CHANGELOG.md`) — please verify the Release PR diff before merging it.
- [ ] After release, on a fresh `npx skills add chainbase-labs/agentkey`
install, an out-of-date version should now correctly produce
`UPGRADE_AVAILABLE <old> <new>` and trigger the existing AskUserQuestion
prompt flow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 15:48:37 +08:00
lxcong 7e5f670984 ci: drop component prefix from release tags (#8)
## Summary

The v1.1.0 release-please tag came out as `agentkey-skill-v1.1.0`
instead of `v1.1.0`, because `release-please` defaults
`include-component-in-tag: true` for simple release-type. That created
inconsistency with v1.0.0's bare `v1.0.0` tag (which was hand-tagged
during migration cutover).

I've already renamed the v1.1.0 tag + release to match v1.0.0's
convention. This PR ensures future releases stay consistent by adding
`include-component-in-tag: false`.

## Test plan

- [x] `python3 -c "import json;
json.load(open('release-please-config.json'))"` OK
- [x] Existing `v1.1.0` and `v1.0.0` tags are both bare-`v` format
(verified via `gh api repos/.../tags`)
- [ ] Next release-please-cut release uses `vX.Y.Z` format, not
`agentkey-skill-vX.Y.Z`

Co-authored-by: lxcong <lxhtheresa@gmail.com>
2026-04-23 14:34:05 +08:00
lxcong 5dd7ad6b56 fix: rename version to version.txt for release-please auto-bump (#7)
## Summary

Release-please PR #6 (bumping to v1.1.0) revealed that the `version`
file wasn't being auto-updated — `plugin.json`, `CHANGELOG.md`, and
`.release-please-manifest.json` all bumped correctly, but `version`
stayed at `1.0.0`.

Root cause: the `generic` extra-files updater requires `#
x-release-please-version` anchor markers in the file. Our `version` file
is a bare version string with no markers, so release-please silently
skipped it.

Consequence if left unfixed: `check-update.sh` reads `version` to
compare with remote releases — users would see "UPDATED: v1.1.0" on
every skill invocation after v1.1.0 publishes, because the local
`version` file never advances.

## Fix

- Rename `version` → `version.txt`. The `simple` release-type in
release-please auto-manages `version.txt` by convention, no extra-files
entry needed.
- Drop the obsolete `{"type": "generic", "path": "version"}` entry from
`release-please-config.json`.
- Update `check-update.sh` VERSION_FILE pointer.
- Set `version.txt` to `1.1.0` so it matches what PR #6 will cut.

## Merge order

This PR first, **then** release PR #6. Release-please will see
`version.txt=1.1.0` already in place and leave it alone; `plugin.json`
will still bump from 1.0.0 → 1.1.0 as usual.

## Test plan

- [x] `python3 -c "import json;
json.load(open('release-please-config.json'))"` OK
- [x] `bash -n skills/agentkey/scripts/check-update.sh` OK
- [x] `git mv` preserves history
- [ ] Post-merge + post-release-PR: `git show v1.1.0:version.txt` equals
`1.1.0`

Co-authored-by: lxcong <lxhtheresa@gmail.com>
2026-04-23 14:30:08 +08:00
lxcong b3d806105b chore: initial public release 2026-04-23 13:52:31 +08:00