Skip to content

Commit 69cdeea

Browse files
committed
fix(security): commit-pin GitHub skill-pack installs (taste-skill, remotion, higgsfield)
The skills CLI (v1.5.14) has no ref-pinning syntax, so install_skill_pack_pinned clones + checks out an audited SHA and installs each SKILL.md by its name: frontmatter, replicating the CLI's discovery layout. Higgsfield clone pinned too (was unpinned HEAD). Sandbox-tested: 13/13 taste skills at pin; bad SHA soft-fails clean.
1 parent eb2cdaf commit 69cdeea

3 files changed

Lines changed: 93 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
2323
- Git history rewrite: `git filter-repo` collapsed all author/committer identities into a single `the operator <operator@example.com>` identity across `main` and the v1.0.0 tag. All `Co-authored-by:` trailers stripped. Tag commit hash for v1.0.0 changed; this repo has no published npm artifact, so no downstream impact.
2424

2525
### Fixed
26-
- **MCP registrations version-pinned at install time** (rug-pull defense, /safetycheck Checks 22/24 — same remediation as cli-maxxing). New `pin_npm` helper in `design/install.sh` + `media/install.sh` resolves the currently published version via `npm view` and registers `pkg@x.y.z` instead of a floating ref, so the command written into Claude's config stops re-resolving to whatever is newest on npm at every session start. Covers all five npm-backed registrations: `@21st-dev/magic` + `@playwright/mcp` (design, previously `@latest`) and `@kimtaeyoon83/mcp-server-youtube-transcript` + `@kevinwatt/yt-dlp-mcp` + `whisper-mcp` (media, previously `@latest`/versionless). Falls back to `@latest` only when npm/network is unavailable at install time. Known remaining floats, deferred deliberately: the `npx skills add` installs (taste-skill, remotion) — the skills CLI documents no ref-pinning syntax — and the one-shot `@anthropic-ai/claude-code` invoker fallback, which runs once at install and never persists into config.
26+
- **MCP registrations version-pinned at install time** (rug-pull defense, /safetycheck Checks 22/24 — same remediation as cli-maxxing). New `pin_npm` helper in `design/install.sh` + `media/install.sh` resolves the currently published version via `npm view` and registers `pkg@x.y.z` instead of a floating ref, so the command written into Claude's config stops re-resolving to whatever is newest on npm at every session start. Covers all five npm-backed registrations: `@21st-dev/magic` + `@playwright/mcp` (design, previously `@latest`) and `@kimtaeyoon83/mcp-server-youtube-transcript` + `@kevinwatt/yt-dlp-mcp` + `whisper-mcp` (media, previously `@latest`/versionless). Falls back to `@latest` only when npm/network is unavailable at install time. Known remaining float, deferred deliberately: the one-shot `@anthropic-ai/claude-code` invoker fallback, which runs once at install and never persists into config.
27+
- **GitHub skill-pack installs commit-pinned** (closes the deferred `npx skills add` float from the entry above). New `install_skill_pack_pinned` helper (design + media): clones the pack repo, checks out an audited SHA, and copies each `SKILL.md`'s directory to `~/.claude/skills/<name>/` by its `name:` frontmatter — replicating the skills CLI's `skills/<name>/SKILL.md` discovery deterministically, since the CLI (v1.5.14) has no ref-pinning syntax. Pins: taste-skill @ `06d6028` (13 skills at this commit; the 8 canonical names still anchor the count check), remotion-dev/skills @ `8dad6ec`, and the Higgsfield seedance repo clone @ `83dcb10` (was an unpinned `--depth 1` HEAD clone). Sandbox-tested against the live taste-skill repo: 13/13 installed at the pin; a bad SHA soft-fails without partial installs. Bump the `*_COMMIT` constants to update.
2728
- Printed manual-install curl commands for the whisper model (`media/install.sh` help strings) now carry `--proto '=https' --proto-redir '=https'` so copy-pasted commands match the hardened executed ones.
2829
- Excalidraw MCP URL aligned with the official OSS README (no more pointing at the wrong upstream).
2930
- Added `set -e` to `design/install.sh` so a partial install fails loud instead of silently leaving missing tools on disk.

design/install.sh

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,44 @@ pin_npm() {
3939
if [ -n "$v" ]; then echo "$1@$v"; else echo "$1@latest"; fi
4040
}
4141

42+
# Install a GitHub skill pack at a pinned commit (rug-pull defense). The
43+
# `skills` CLI (v1.5.14) has no ref-pinning syntax, so we clone + checkout the
44+
# audited SHA ourselves and copy each SKILL.md's directory to
45+
# ~/.claude/skills/<name>/ using its `name:` frontmatter — replicating the
46+
# CLI's default skills/<name>/SKILL.md discovery deterministically.
47+
# Args: $1 = repo URL, $2 = pinned commit SHA, $3 = label
48+
install_skill_pack_pinned() {
49+
local url="$1" sha="$2" label="$3"
50+
local tmp
51+
tmp="$(mktemp -d)"
52+
if ! git clone --quiet "$url" "$tmp" 2>/dev/null; then
53+
rm -rf "$tmp"
54+
soft_fail "$label: clone failed ($url)"
55+
return 1
56+
fi
57+
if ! git -C "$tmp" checkout --quiet "$sha" 2>/dev/null; then
58+
rm -rf "$tmp"
59+
soft_fail "$label: pinned commit $sha not found upstream"
60+
return 1
61+
fi
62+
local search_root="$tmp"
63+
if [ -d "$tmp/skills" ]; then search_root="$tmp/skills"; fi
64+
local installed=0
65+
local skill_md name dest
66+
while IFS= read -r skill_md; do
67+
name="$(sed -n 's/^name:[[:space:]]*//p' "$skill_md" | head -1 | tr -d '"' | tr -d "'")"
68+
if [ -z "$name" ]; then continue; fi
69+
dest="$HOME/.claude/skills/$name"
70+
if [ -f "$dest/SKILL.md" ]; then continue; fi
71+
mkdir -p "$dest"
72+
cp -R "$(dirname "$skill_md")/." "$dest/"
73+
installed=$((installed + 1))
74+
done < <(find "$search_root" -name SKILL.md -not -path '*/.git/*' 2>/dev/null)
75+
rm -rf "$tmp"
76+
success "$label installed ($installed new, pinned @ ${sha:0:7})"
77+
return 0
78+
}
79+
4280
# -----------------------------------------------------------------------------
4381
# Detect OS
4482
# -----------------------------------------------------------------------------
@@ -124,7 +162,9 @@ install_uiux_skill() {
124162

125163
# -----------------------------------------------------------------------------
126164
# Install Taste Skill (Leonxlnx/taste-skill)
127-
# Upstream ships 8 skills whose `name:` frontmatter drives their installed path:
165+
# Upstream ships 13 skills at the pinned commit, whose `name:` frontmatter
166+
# drives their installed path. The 8 canonical ones below anchor the
167+
# installed-count verification:
128168
# design-taste-frontend, high-end-visual-design, full-output-enforcement,
129169
# redesign-existing-projects, stitch-design-taste, minimalist-ui,
130170
# industrial-brutalist-ui, gpt-taste
@@ -160,16 +200,12 @@ install_taste_skill() {
160200

161201
info "Installing Taste Skill pack (Leonxlnx/taste-skill)..."
162202

163-
local TASTE_SKILL_URL="https://github.com/Leonxlnx/taste-skill"
164-
165-
npx skills add "$TASTE_SKILL_URL" --yes --global 2>/dev/null
203+
# Pinned to an audited commit — bump TASTE_COMMIT to update.
204+
local TASTE_COMMIT="06d6028b5c623016c59ce8536f578e5a1127b499"
205+
install_skill_pack_pinned "https://github.com/Leonxlnx/taste-skill" "$TASTE_COMMIT" "Taste Skill pack" || true
166206

167207
local after_count
168208
after_count="$(taste_installed_count)"
169-
if [ "$after_count" -lt 8 ]; then
170-
npx skills add "$TASTE_SKILL_URL" --yes 2>/dev/null
171-
after_count="$(taste_installed_count)"
172-
fi
173209

174210
if [ "$after_count" -ge 8 ]; then
175211
success "Taste Skill installed ($after_count/8 variants under ~/.claude/skills/)"

media/install.sh

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,44 @@ pin_npm() {
4343
if [ -n "$v" ]; then echo "$1@$v"; else echo "$1@latest"; fi
4444
}
4545

46+
# Install a GitHub skill pack at a pinned commit (rug-pull defense). The
47+
# `skills` CLI (v1.5.14) has no ref-pinning syntax, so we clone + checkout the
48+
# audited SHA ourselves and copy each SKILL.md's directory to
49+
# ~/.claude/skills/<name>/ using its `name:` frontmatter — replicating the
50+
# CLI's default skills/<name>/SKILL.md discovery deterministically.
51+
# Args: $1 = repo URL, $2 = pinned commit SHA, $3 = label
52+
install_skill_pack_pinned() {
53+
local url="$1" sha="$2" label="$3"
54+
local tmp
55+
tmp="$(mktemp -d)"
56+
if ! git clone --quiet "$url" "$tmp" 2>/dev/null; then
57+
rm -rf "$tmp"
58+
soft_fail "$label: clone failed ($url)"
59+
return 1
60+
fi
61+
if ! git -C "$tmp" checkout --quiet "$sha" 2>/dev/null; then
62+
rm -rf "$tmp"
63+
soft_fail "$label: pinned commit $sha not found upstream"
64+
return 1
65+
fi
66+
local search_root="$tmp"
67+
if [ -d "$tmp/skills" ]; then search_root="$tmp/skills"; fi
68+
local installed=0
69+
local skill_md name dest
70+
while IFS= read -r skill_md; do
71+
name="$(sed -n 's/^name:[[:space:]]*//p' "$skill_md" | head -1 | tr -d '"' | tr -d "'")"
72+
if [ -z "$name" ]; then continue; fi
73+
dest="$HOME/.claude/skills/$name"
74+
if [ -f "$dest/SKILL.md" ]; then continue; fi
75+
mkdir -p "$dest"
76+
cp -R "$(dirname "$skill_md")/." "$dest/"
77+
installed=$((installed + 1))
78+
done < <(find "$search_root" -name SKILL.md -not -path '*/.git/*' 2>/dev/null)
79+
rm -rf "$tmp"
80+
success "$label installed ($installed new, pinned @ ${sha:0:7})"
81+
return 0
82+
}
83+
4684
# -----------------------------------------------------------------------------
4785
# Detect OS
4886
# -----------------------------------------------------------------------------
@@ -95,18 +133,14 @@ install_remotion_skills() {
95133
return
96134
fi
97135

98-
npx skills add remotion-dev/skills --yes --global 2>/dev/null
136+
# Pinned to an audited commit — bump REMOTION_COMMIT to update.
137+
local REMOTION_COMMIT="8dad6ec5c5c7cedee4d2aa620bb68386f8fe8eb9"
138+
install_skill_pack_pinned "https://github.com/remotion-dev/skills" "$REMOTION_COMMIT" "Remotion skills" || true
99139

100140
if [ -d "$HOME/.claude/skills/remotion-best-practices" ] || [ -L "$HOME/.claude/skills/remotion-best-practices" ]; then
101141
success "Remotion skills installed for Claude Code"
102142
else
103-
npx skills add remotion-dev/skills --yes 2>/dev/null
104-
105-
if [ -d "$HOME/.claude/skills/remotion-best-practices" ] || [ -L "$HOME/.claude/skills/remotion-best-practices" ]; then
106-
success "Remotion skills installed"
107-
else
108-
soft_fail "Remotion skills installation could not be verified"
109-
fi
143+
soft_fail "Remotion skills installation could not be verified"
110144
fi
111145
}
112146

@@ -155,8 +189,11 @@ install_higgsfield_skills() {
155189
# local, so guard the expansion or set -u kills the run after this returns.
156190
trap 'rm -rf "${_TMP:-}"' RETURN
157191

158-
if ! git clone --quiet --depth 1 https://github.com/beshuaxian/higgsfield-seedance2-jineng.git "$_TMP" 2>/dev/null; then
159-
soft_fail "Could not clone Higgsfield repo — skipping. Install manually: https://github.com/beshuaxian/higgsfield-seedance2-jineng"
192+
# Pinned to an audited commit (rug-pull defense) — bump HIGGS_COMMIT to update.
193+
local HIGGS_COMMIT="83dcb10ee38c9694ac0f455ec55a62f2be3b8a14"
194+
if ! git clone --quiet https://github.com/beshuaxian/higgsfield-seedance2-jineng.git "$_TMP" 2>/dev/null \
195+
|| ! git -C "$_TMP" checkout --quiet "$HIGGS_COMMIT" 2>/dev/null; then
196+
soft_fail "Could not clone Higgsfield repo at pinned commit ${HIGGS_COMMIT:0:7} — skipping. Install manually: https://github.com/beshuaxian/higgsfield-seedance2-jineng"
160197
return
161198
fi
162199

0 commit comments

Comments
 (0)