#3610 added a `bundled-gsd-hook` classifier to classifyPromptUserAction
that auto-removes blocked hook files on first-time-baseline scan. The
match shape was a regex `/^hooks\/gsd-[^/]+\.(?:js|sh|cjs|mjs)$/` that
matched ANY file under hooks/ named gsd-<name>.{js,sh,cjs,mjs}, not only
the 13 hooks the npm package actually ships. As a result the classifier
silently auto-classified — and the resolver auto-removed — user-authored
custom hooks (hooks/gsd-personal-experiment.js) and retired bundled hooks
from prior versions (hooks/gsd-old-statusline.js).
Evidence the maintainer was already working around this: 0862df15 (the
#3610 follow-up) renamed the integration-test fixture
hooks/gsd-retired-hook.js -> hooks/gsd-retired-hook.txt specifically to
dodge the classifier so the test could exercise the "explicit block" path
it was written for.
Fix: replace the shape regex with an explicit Set of the 13 shipped hook
filenames (BUNDLED_GSD_HOOK_FILES). Files outside the whitelist fall
through to the existing block-or-prompt flow so users retain control.
A regression guard (tests/bug-3628-bundled-hook-classifier-whitelist.test.cjs)
fails CI if the whitelist drifts from the on-disk hooks/ directory in
either direction: whitelisted-but-missing OR shipped-but-not-whitelisted.
The latter check uses the SAME shape regex the buggy classifier used,
re-purposed as a lint that ensures every gsd-*-shaped file shipped in the
distribution IS in the whitelist.
Behavioural tests cover: every entry in BUNDLED_GSD_HOOK_FILES classifies
as bundled-gsd-hook -> remove; six user-owned / retired filenames return
null (proves the regression is fixed); nested hooks/gsd-*/ directories
still return null (the #3610 nested-directory boundary stays intact);
non-gsd hooks still return null (the user-hook-preservation boundary
stays intact).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 lines
901 B
Markdown
6 lines
901 B
Markdown
---
|
|
type: Security
|
|
issue: 3628
|
|
---
|
|
**Installer no longer auto-removes user-authored or retired `hooks/gsd-*` files** — the `bundled-gsd-hook` classifier added in #3610 used a shape regex (`/^hooks\/gsd-[^/]+\.(?:js|sh|cjs|mjs)$/`) that matched any file with that naming shape, not only the 13 hooks actually shipped in the npm package. User-authored custom hooks (e.g. `hooks/gsd-personal-experiment.js`) and retired bundled hooks from prior versions were silently auto-classified and removed on first-time-baseline scan. The classifier now whitelists the explicit set of shipped hook filenames (`BUNDLED_GSD_HOOK_FILES`); files outside the whitelist fall through to the existing block-or-prompt flow so the user retains control. A drift guard in `tests/bug-3628-bundled-hook-classifier-whitelist.test.cjs` fails CI if the whitelist diverges from the on-disk `hooks/` directory in either direction.
|