news 2026/9/5 16:59:56

从形状正则到显式白名单:get-shit-done 安装器 3628 修复如何防止 Hook 误删

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从形状正则到显式白名单:get-shit-done 安装器 3628 修复如何防止 Hook 误删

从形状正则到显式白名单:get-shit-done 安装器 #3628 修复如何防止 Hook 误删

【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done

本文基于仓库变更集文档 3628-bundled-hook-whitelist.md(类型标记为 Security,关联 issue #3628),解析 get-shit-done 安装器迁移层的一次关键安全修复:bundled-gsd-hook分类器为何不能依赖"命名形状正则"自动删除用户目录下的hooks/gsd-*文件,以及显式白名单BUNDLED_GSD_HOOK_FILES+ CI 漂移守卫如何保证"自动删除范围 == 实际发布文件集合"。读完本文,你能理解该安装器prompt-user阻塞流程的完整决策链,并掌握"白名单 + 双向漂移测试"这一可复用的防误删模式。

背景:#3610 修复了"升级中断",却引入了"静默误删"

要理解 #3628,需要先理解它修补的对象。get-shit-done 提供一层**安装器迁移(installer migration)**架构(见 docs/installer-migrations.md),其设计目标第一条就是"Protect user data by default",并区分三类文件:

  • Managed file:GSD 安装并记录在安装清单(manifest)中的文件,未被修改时可自动替换;
  • User-owned file:用户自己创建的文件,"绝不能因为恰好位于 GSD 目录下就被删除";
  • Unknown file:既不在清单中、又未被归类为用户所有的文件,除非有明确证据被迁移显式归类,否则一律保留。

此前 #3610 解决了一个"升级硬中断"问题:全新执行npx get-shit-done-cc@latest --codex时,目标目录(如~/.codex/hooks/)里残留着上一版本 GSD 自带的 hook 文件,安装器清单无法证明这些文件"受管理",于是被归类为"GSD 风格文件但无清单证据,需要用户显式选择",assertInstallerMigrationsUnblocked直接抛错终止升级(见 tests/bug-3610-installer-migration-bundled-hooks-classification.test.cjs 的文件头注释)。

#3610 的修复是在分类器classifyPromptUserAction中新增bundled-gsd-hook类别:凡是形如hooks/gsd-<name>.{js,sh,cjs,mjs}的文件都自动判定为"官方自带 hook",默认为remove(安装器随后会写入新版本覆盖)。问题在于它使用的是形状正则

/^hooks\/gsd-[^/]+\.(?:js|sh|cjs|mjs)$/

这条正则匹配的是"任何符合该命名形状的文件",而不是 npm 包中实际发布的 13 个 hook。变更集文档 3628-bundled-hook-whitelist.md 明确指出受害对象有两类:

  1. 用户自写的自定义 hook,例如hooks/gsd-personal-experiment.js
  2. 旧版本 GSD 发布、但当前版本已退役的 hook 文件

这两类文件在首次基线扫描(first-time-baseline scan)时会被静默自动分类并删除——属于无声的数据丢失(silent data loss),这正是该变更集被标记为type: Security的原因。

修复实现:从"匹配形状"改为"白名单精确命中"

修复落在 get-shit-done/bin/lib/installer-migration-report.cjs。分类器不再依赖正则形状,而是查询一个冻结的显式白名单集合,与 npm 发行版中hooks/目录下实际发布的 13 个文件一一对应:

// #3628: explicit whitelist of bundled hook files shipped in the npm // distribution under `hooks/`. const BUNDLED_GSD_HOOK_FILES = Object.freeze(new Set([ 'hooks/gsd-check-update-worker.js', 'hooks/gsd-check-update.js', 'hooks/gsd-context-monitor.js', 'hooks/gsd-graphify-update.sh', 'hooks/gsd-phase-boundary.sh', 'hooks/gsd-prompt-guard.js', 'hooks/gsd-read-guard.js', 'hooks/gsd-read-injection-scanner.js', 'hooks/gsd-session-state.sh', 'hooks/gsd-statusline.js', 'hooks/gsd-update-banner.js', 'hooks/gsd-validate-commit.sh', 'hooks/gsd-workflow-guard.js', ]));

可以核对仓库根目录 hooks/ 目录:其中 13 个gsd-*.{js,sh}文件与白名单完全一致,另有lib/子目录(两个辅助文件),不会被[^/]+的形状约束误匹配。

两个实现细节值得注意:

  • Object.freeze(new Set([...])):白名单是模块级不可变常量("single point of truth"),测试会断言它必须以Set类型导出,保证调用方可以用has()做 O(1) 成员探测,且运行期无法被意外篡改;
  • 白名单条目强制使用 POSIX 斜杠且带hooks/前缀,与分类器接收的relPath规范化形态严格对齐(见下文测试断言)。

分类器的完整决策链:哪些文件自动处理,哪些交还用户

classifyPromptUserAction(installer-migration-report.cjs)对每个被阻塞的prompt-user动作按relPath做三类判定,未命中任何一类则返回null,落入既有的"阻塞或提示"(block-or-prompt)流程,用户保留控制权

判定条件返回类别 / 默认选择
过期的 SDK 构建产物relPath匹配^get-shit-done/sdk/(dist|src)/stale-sdk-build-artifactremove(每次安装都会重新生成,删除无损)
用户可见的 skill 锚点relPath匹配^skills/gsd-[^/]+/SKILL\.md$user-facing-skillkeep(用户自有内容必须保留)
官方自带 hook(#3628 白名单)BUNDLED_GSD_HOOK_FILES.has(relPath)bundled-gsd-hookremove
以上皆否——null(不自动分类,走人工确认)

修复后白名单分支的注释把安全边界写得很直白:与形状匹配但不在白名单内的文件(用户自写 hook、旧版本退役 hook)fall through 到 block-or-prompt 流程。

当白名单命中后,动作由materializeResolution(installer-migration-report.cjs)具体化:

  • keepbaseline-preserve-user(幂等保留);
  • removebackup-and-remove——删除前先在迁移日志目录gsd-migration-journal/<runId>-backups/留一份回滚备份。即使"自动删除",也不是物理抹除,这一点与 docs/installer-migrations.md 中backup-and-remove动作类型的定义("用户会得到清晰报告并可检查备份")一致。

非交互(non-TTY)场景由resolveInstallerMigrationPromptsForNonTty(installer-migration-report.cjs)驱动:当安装器无法交互式提问时,优先读取环境变量GSD_INSTALLER_MIGRATION_RESOLVE(取值keep/remove)作为操作者覆写;未设置时才走上述分类器安全默认值。无法安全默认的动作会被过滤回result.blocked,最终由assertInstallerMigrationsUnblocked抛出按原因分组的错误信息(每组最多展示 3 条示例路径),并提示"设置GSD_INSTALLER_MIGRATION_RESOLVE=<choice>可在非交互环境下解决"。换言之,白名单机制把"可无脑处理的文件"和"必须人来拍板的文件"切开了:白名单内的 13 个文件自动清理,白名单外的一切文件升级不会静默动、而是阻塞并给出可操作的解决途径。

CI 漂移守卫:白名单必须与磁盘上的hooks/双向一致

变更集文档强调了配套防线:tests/bug-3628-bundled-hook-classifier-whitelist.test.cjs 是一个"漂移守卫",任何方向的漂移都会让 CI 失败

  1. 白名单 → 磁盘方向(test 第 66-79 行):遍历BUNDLED_GSD_HOOK_FILES每个条目,断言对应的hooks/<文件名>在磁盘上真实存在——防止"删除了某个 hook 文件却忘了从白名单移除";
  2. 磁盘 → 白名单方向(test 第 81-93 行):用readdirSync扫描hooks/下所有gsd-*.{js,sh,cjs,mjs}文件,断言每个都在白名单内——防止"新增发布 hook 却忘了加白名单"(后果是该 hook 在升级时反而会被当作未知文件阻塞,或者在旧版本逻辑下被误当用户文件);
  3. 结构约束(test 第 37-64 行):白名单必须是Set、非空,且每个条目都以hooks/开头、使用 POSIX 斜杠、包含gsd-前缀。

测试还覆盖了负向与边界情形,确保修复没有破坏 #3610 既有边界(test 第 108-139 行):

  • hooks/gsd-personal-experiment.jshooks/gsd-my-custom-guard.shhooks/gsd-team-policy.cjshooks/gsd-retired-hook.jshooks/gsd-old-statusline.jshooks/gsd-experimental.mjs这 6 个用户自有/退役样例必须返回null(不得自动分类);
  • 嵌套目录hooks/gsd-helpers/index.js不得被分类(保留 #3610 引入的层级边界);
  • gsd-前缀的hooks/my-custom-hook.js不得被分类。

正向测试则遍历白名单全部 13 项,断言每一项的分类结果恰好是{ category: 'bundled-gsd-hook', choice: 'remove' }

小结:一条值得借鉴的"自动删除安全边界"

#3628 的教训可以浓缩为一句话:任何"自动删除/覆盖"逻辑的判定依据必须是"发行版实际发布物清单",而不是"命名形状相似性"。形状正则(hooks/gsd-*.js)只能回答"它看起来像官方文件",白名单才能回答"它就是官方文件"。其工程组合是:

  1. 单一事实来源:Object.freeze的显式Set白名单,与发行版hooks/目录一一对应;
  2. 分类器未命中即返回null,把控制权交还 block-or-prompt 流程,用户文件永不静默消失;
  3. 即使命中白名单,删除路径也是backup-and-remove,迁移日志保留回滚备份;
  4. 双向漂移守卫测试锁死"白名单 ↔ 磁盘发行物"的同步关系,任何一方改动都会立刻在 CI 暴露。

相关延伸阅读:安装器迁移架构 docs/installer-migrations.md、非 TTY 解析的早期规格变更集 3541-installer-migration-prompt-user-resolution.md、上游问题修复测试 tests/bug-3610-installer-migration-bundled-hooks-classification.test.cjs 与 #3610 变更集 3610-codex-install-bundled-hooks-blocker.md。

【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 16:58:15

树莓派5与深度相机构建无接触3D腰臀围测量仪

这次我们来看一个把树莓派5当嵌入式主机、把深度相机当测量探头、把点云算法当量尺的完整方案&#xff1a;无接触 3D 智能腰臀围测量仪。它的核心不是“拉软尺”&#xff0c;也不是让测量员贴身去量&#xff0c;而是让被测者站到设备前面&#xff0c;3D 相机采集人体表面点云&a…

作者头像 李华
网站建设 2026/9/5 16:57:39

基于Vue+SpringBoot+MySQL的超市商品管理系统全栈开发实战

简介&#xff1a;这是一套面向计算机专业本科生的高分毕业设计级超市商品管理系统&#xff0c;采用VueSpringBootMySQL技术栈实现前后端分离架构&#xff0c;适用于毕设开发、课程设计及Java全栈实战学习。资源包共包含源码、MySQL数据库脚本、详细功能文档、开题报告与外文文献…

作者头像 李华
网站建设 2026/9/5 16:57:15

基于uni-app与Serverless的英语学习小程序全栈开发实践

简介&#xff1a;这是一套面向英语学习者与小程序开发者的微信小程序源码项目&#xff0c;聚焦考研英语备考、日常词汇积累与口语能力提升三大核心场景。项目基于uniapp前端框架与uniCloud Serverless云服务架构实现&#xff0c;具备生词本管理、拍照识别单词、离线词典查询、A…

作者头像 李华
网站建设 2026/9/5 16:57:06

技术博客选题落地:从影视特效到可复现的工程实践

没法把“蜘蛛侠vs超人”写成技术博客。这个选题面向的是影视角色比较&#xff0c;不属于可落地、可复现的工程主题&#xff0c;和本博客定位不一致。 如果想在技术社区写作&#xff0c;围绕这个方向可以换成这些可展开的选题&#xff1a; 从 OpenGL / Vulkan 渲染管线理解超人…

作者头像 李华
网站建设 2026/9/5 16:53:17

抖音快手点赞平台源码技术深度解析与实战避坑指南

简介&#xff1a;这是一套面向短视频运营从业者与PHP开发者的技术型源码资源&#xff0c;用于快速搭建抖音、快手、火山等平台的视频点赞任务分发与管理平台&#xff0c;解决多账号任务调度、用户激励与数据统计等核心运营需求。资源包共2000个文件&#xff0c;主体为1068个PHP…

作者头像 李华