launch-guard
在将私有git存储库公开之前检查它,并且永远不要打印它找到的内容。
npx -y @voyageurworks/launch-guard path/to/your/repo
您需要Node 22或更新的版本。它从不编辑存储库,唯一的网络步骤(只读 git ls-remote)可以用 --offline。退出密码是 0 当没有什么阻碍出版时, 1 当事情发生时。
它是什么样子的
的一次性存储库上的实际输出 app.js 持有一个假的aws式密钥:
BLOCK (2)
app.js:1 secret format
4e4cc92:app.js:1 secret in history
WARN (4)
list private list has no entries
LICENSE missing LICENSE
SECURITY missing SECURITY
CI missing CI
INFO (1)
remote no remote
RESULT: FAIL
它说明了哪个文件和行,什么类型的问题,以及旧的提交包含相同的内容(一旦存储库是公共的,删除行是不够的)。它不打印密钥、密钥的任何部分或提交消息。
在30秒内尝试同样的事情,没有真正的秘密(bash, macOS, Linux或Git bash;你的提交id会有所不同):
mkdir launch-guard-demo && cd launch-guard-demo && git init -q
printf "const k = 'AKIA%s';\n" ABCDEFGHIJKLMNOP > app.js
git add . && git -c user.name=demo -c user.email=demo commit -qm demo
npx -y @voyageurworks/launch-guard . --offline
通过 旅客的作品。麻省理工学院的执照。
为什么
越来越多的存储库是由人工智能助手编写的,而这个助手也是你会问“这个发布安全吗?”问题是普通扫描仪会打印出它找到的东西。如果人工智能运行它,你的密钥或家庭地址现在就在聊天中。
发射防护装置的建造就是为了防止这种情况发生。它只报告 哪里 有些东西是(文件和行), 什么样 问题是,您的私有列表的哪个条目匹配。它不打印秘密,它的任何部分,列表中的值,作者名称或提交消息。它永远不会改变你的文件,改写历史或推送。唯一的网络用途是只读 git ls-remote 针对您配置的远程(使用 --offline),并且它写入的唯一文件是来自的模板 --init-list.
排在gitleaks和TruffleHog旁边
发射防护装置不能取代普通的秘密扫描仪;也运行这些。他们是为其他工作而生的。右边列中的所有内容都来自它们自己的readme(选中2026-11-02)。
| launch-guard | gitleaks, TruffleHog | |
|---|---|---|
| 主要问题 | “这个存储库公开安全吗?” | “这些代码、历史或数据源中的秘密在哪里?” |
| 这一发现说明了什么 | 文件和行,有点问题,哪个列表条目匹配。绝不是值、作者或提交消息。 | gitleaks的记录 git -v 输出显示匹配的秘密、作者、电子邮件和提交。 |
| 你自己的私语(姓名、地址、学校、密码) | 从存储库外部的私有列表中检查 | 不是他们的目的(gitleaks可以自定义规则) |
| 它看起来在哪里 | 一个git存储库:文件、历史、标签、refg、剩余refs | Gitleaks: git存储库、目录和标准输入。TruffleHog还可以扫描聊天记录、维基、对象存储等等 |
| 秘密的格式 | 一组集中的已知格式加上诸如key或token之类的单词旁边的赋值 | TruffleHog分类800+秘密类型,可以检查凭证是否仍然有效 |
| 由人工智能助手操作 | 是:输出可以安全地出现在聊天中 | 而不是它们的设计目标 |
使用gitleaks或TruffleHog进行连续扫描并检查实时凭据。使用launch-guard作为你按下“公开”之前的最后一次检查,当AI助手在运行它时。
使用它作为克劳德密码或法典技能
第一次运行,没有设置
npx -y @voyageurworks/launch-guard <repo>
(从一个克隆人, node skill/launch-guard.mjs <repo> 也是一样的。)取代 <repo> 使用存储库的路径(省略它以扫描当前文件夹)。添加 --offline 跳过一个网络步骤(参见下面的“网络和文件”)。将它指向存储库的顶部文件夹:对于普通文件夹或存储库的子文件夹,它会跳过历史记录和旧提交检查并显示出来。由于还没有私有列表,它仍然检查秘密、可访问的旧提交和基本内容,并明确表示跳过了个人检查和限制检查。
在脚本中,在推送之前,以及在CI中
--json打印与一个JSON文档相同的报告(version,result,counts,findings),每个发现都有相同的六个固定字段和相同的承诺:没有秘密,没有列表值,没有提交消息。出口密码是一样的,所以node skill/launch-guard.mjs . --json --offline > report.json适用于任何脚本。--print-hook打印一个gitpre-push挂钩到屏幕上。它什么也没写;你把它保存为.git/hooks/pre-push(该工具从不编辑存储库),并将launch-guard.mjs进去。任何阻塞的发现都将停止推动。- 文档/ ci.md 有一个现成的GitHub Actions作业。运行器没有私有列表,所以CI包含秘密、历史、旧提交和基础;个人和有限的支票留在你自己的机器上。
你的私人列表
为了捕捉你自己的名字、地址、学校、家庭或码字,该工具需要一个列表。该列表位于repo之外,因此它永远不会意外发布。
node skill/launch-guard.mjs --init-list
这将创建一个模板 ~/.config/launch-guard/private-list.txt。它不会覆盖文件,也不会在git存储库中写入。打开它 你自己的编辑 每行添加一个条目,格式如下 kind|regex:
personal|zq-example-name
restricted|zq-example-codeword
kind 是 personal 或者 restricted。之后的部分 | 是一个正则表达式,匹配时不考虑大小写。开头的行 # 空行被忽略。要在另一个地方使用列表,请添加 --list <file>.
永远不要将列表粘贴到聊天中,也不要让助手打开它。 助手不需要这些值。报告将显示“列表条目3”,您可以自己查找第3行。
四项检查
- 的秘密。 已知的密钥格式和长随机字符串旁边的字,如密钥,令牌或密码,在您当前的文件。它跟随一个值,该值位于比它的键(一个YAML块、一个延续或一个用分隔的字符串)的后面一行
+或者一个反斜杠(最多四行),读取包含符号的引号值,并且知道常见提供者(云、支付、聊天和开发人员令牌)的格式Authorization: Bearer头。 - 个人资料及限制条款。 您的私人列表中的所有内容,与文件内容和文件和文件夹名称相匹配。
- 历史。 在旧版本的文件、提交消息、带注释的标签消息和标记器、作者姓名和电子邮件以及合并提交在自动合并之外的更改(例如在解决冲突时输入的秘密)中检查每个分支、标签和reflog是否有相同的秘密和列表条目。合并检查需要git 2.36或更新版本;在旧的git上写着“历史不完整”。
- 旧的提交仍然可访问。 剩余的重写引用,额外的引用和隐藏的远程拉取请求引用,在历史重写后仍然可以下载,加上本地不可达对象(提示检查,而不是泄漏的证明)。
它还运行一些基础内容:许可证的存在、安全文件和CI、启动的资金文件以及用户配置文件文件夹的绝对路径(例如 C:\Users\<name>)的辅助文件,如 CLAUDE.md.
阅读报告
每一行看起来像 path:line label (list entry N),按级别分组。最后一行是 RESULT: PASS 或者 RESULT: FAIL (与 --json, result 现场说 pass 或者 fail 相反)。
| 标签 | 这意味着什么? | 该怎么做 |
|---|---|---|
| 秘密的格式 | 一种形状像已知密钥或标记的字符串 | 将其从文件中删除,然后将密钥视为公开的,并在其提供程序中替换它 |
| 秘密近关键字 | 在像key, token或password这样的单词旁边的一个看起来随机的长值 | 自己检查线路;移除它,或者在无害的情况下标记为允许使用(见下文) |
| 跟踪的env文件 | 一、 .env 文件被git跟踪(它的内容永远不会被读取) | 停止追踪它并将其排除在回购之外;替换其中的任何秘密 |
| 个人细节 | 一、 personal 列表中的条目在文件中匹配 | 自己编辑台词 |
| 受限制的词 | 一、 restricted 列表中的条目在文件中匹配 | 自己编辑台词 |
| 路径匹配列表 | 与列表匹配的文件或文件夹名称(如 [redacted]) | 重命名它 |
| 回购清单 | 您的私人列表在正在扫描的文件夹内 | 把它移出仓库 |
| 历史的秘密 | 一个秘密出现在旧版本的文件中 | 请参阅下面关于历史的说明 |
| 历史术语 | 列表条目出现在旧的提交、消息、作者或引用名中 | 请参阅下面关于历史的说明 |
| 推荐一个新的存储库 | 重写的余款(refs/original)或远程上隐藏的pull-request ref将保持可访问 | 从没有旧历史的全新存储库发布 |
| 遥不可及的对象 | Git仍然持有一个没有分支到达的对象。准备,修改,改基地,还有 git stash drop 把这些留下,等我收拾干净。它们是本地的,推送不会发布它们 | 警告,而不是泄漏的证明:检查您更改的文件中没有任何敏感内容。要清除它们,请运行 git reflog expire --expire-unreachable=now --all,然后 git gc --prune=now (git gc --prune=now 单独只清除重新编排的blob和丢弃的存储项,因为reflog仍然保留修改或重新定位留下的提交) |
| 额外的裁判 | 当前分支之外的分支、标记或隐藏 | 检查它是否应该是公共的 |
| 不是存储库根 | 该文件夹不是git存储库的顶层(一个普通文件夹,或者另一个存储库的子文件夹),所以历史记录和旧提交检查不会运行 | 在存储库的顶部文件夹上运行该工具 |
| 用户配置文件路径 | 文件保存用户配置文件文件夹的绝对路径 | 将其替换为相对路径或占位符 |
| 缺少许可证,缺少安全,缺少CI | 缺少基本文件 | 添加一个 |
| 资金文件激活 | 一个资金文件被打开 | 在上市之前,确保这是你想要的 |
| 文件被跳过,更多文件被跳过 | 文件太大、二进制文件、符号链接或不可读,因此未读取 | 如果重要的话,你自己看看 |
| 历史不完整,没有承诺,没有遥远 | 该工具无法查看部分历史 | 信息;那部分的检查没有运行 |
| 未扫描嵌套存储库 | 存储库中未跟踪的文件夹本身就是一个git存储库。它的文件和历史不属于此扫描的一部分,并且是普通的 git add 不会发表 | 单独扫描它,或者将其保存在您发布的文件夹之外 |
| 未扫描子模块 | 存储库具有子模块(每个存储库都有另一个)。他们的文件和历史记录不在扫描范围内 | 发布之前在每个子模块中运行该工具 |
| 未检查远程 | 未运行隐藏的拉取请求重新检查: --offline ,否则git在没有提示的情况下无法到达远程。该工具从不要求输入密码,因此需要凭据的私有HTTPS远程通常会在这里登陆 | 从一个shell中运行这个工具,其中git已经拥有该远程的凭据(没有 --offline). 如果曾经将敏感内容推送到存储库,则将其视为需要一个新的存储库 |
| 跳过检查:没有私有列表,私有列表没有条目 | 个人支票和限制支票没有任何用途 | 创建并填写你的清单 |
对于一个无害的匹配(一个测试文件,一个文档化的例子) launch-guard-allow: <reason> 在那一行或上面一行的评论中,有一个真正的原因。只有秘密发现和user-profile-path发现从block降级为warn,并且仅在测试文件(*.test.*, *.spec.*, *_test.*, test_*)及以下 test, tests, spec, specs, fixtures, __tests__, __fixtures__, __mocks__, testdata 和 docs/examples 文件夹。你的私人列表(个人的,受限的,匹配的路径,历史)中的发现总是被封锁在那里:这些是你说过不能公开的东西,所以使用 launch-guard-allow: <reason> 一个真正的例外。具有密钥或凭据扩展名(pem key p12 pfx env crt cer jks keystore asc gpg ppk kdbx)和名字开头 .env 永远不会降低,无论它们的文件夹或名称是什么。
级别和退出代码
- 块必须修理。任何块都会使结果FAIL。
- 警告看看它。
- 信息注释,如跳过的文件或遗漏的列表。
退出代码 0 意味着没有阻塞性发现, 1 意味着至少有一个阻塞发现,并且 2 意味着工具无法运行(错误的路径、错误的列表或内部错误,打印一条固定的消息,没有其他内容)。
PASS 并不能保证存储库是安全的。这意味着这些检查没有发现任何阻塞。
网络和文件
- 唯一的网络用途是只读
git ls-remote --refs针对您已配置的每个远程,查找隐藏的拉取请求参考。它从不提示凭证,并且使用您自己的ssh设置(GIT_SSH_COMMAND,core.sshCommand或者GIT_SSH)就像git一样;为了一个普通的ssh它只添加了批处理模式,所以没有等待密码。跳过它--offline,它添加了一个未检查远程的信息注释(或者,当没有配置远程时,一个“no remote”注释代替)。 - 工具写入的唯一文件是模板
--init-list,只有在没有任何东西并且位置在任何git存储库之外时才会这样做。 - 它不会向其他地方发送任何信息,也没有遥测功能。
不要让你的助手读清单
技能告诉助理永远不要打开你的列表,但技能只是建议。要在Claude Code中强制执行它,请在用户设置中为列表的路径添加拒绝规则(~/.claude/settings.json). 它有这样的形状:
{
"permissions": {
"deny": ["Read(~/.config/launch-guard/private-list.txt)"]
}
}
这遵循Claude Code的许可规则格式 Read(path),在那里 ~/ 意思是你的主文件夹。设置会随着时间的推移而改变,所以要检查电流 权限的文档 在依赖它之前。
2026-10-31在Claude Code 2.1.316上测试,权限模式设置为 bypassPermissions例如,当无头会话要求读取工具读取以这种规则命名的文件时,它仍然被拒绝,而同一文件夹中的控制文件读取正常。所以这个规则在这种模式下也成立;在Claude Code更新后重新检查。
它的作用和不作用是:它阻止了助手的内置文件读取工具,Claude Code还将Read规则应用于命名文件的常见shell命令。它不能可靠地停止助手可以在shell中运行的每个命令(例如,搜索整个文件夹而不命名文件的命令)。为了获得更强的保护,还可以使用Claude Code的沙箱读取限制覆盖路径,或者将列表保存在助手的会话无法到达的地方。 请参阅相同的文档。
Codex有自己的权限和沙盒设置,这些设置应该使助理无法读取列表的位置。使用食典委自己的文件来确定确切的设置;这里不再重复。
诚实的限制
- 聊天已经包含了所有输入或读取的内容。 如果你将秘密或列表粘贴到对话中,没有任何工具可以将其删除。
- 修补漏洞意味着有人看到了线路。 报告告诉你在哪里。你或你的助手仍然需要打开那个位置来改变它。
- 公开类别的存在性。 “在此文件中找到了个人详细信息”本身就是信息,尽管没有显示详细信息。
- 匹配模式的扫描仪无法看到所有东西。 旁边没有关键字的秘密,也没有已知的提供程序格式(例如,变量名为
x),超过四行或在Python圆括号字符串,三引号块,heredoc或模板文字,编码或加密的内容,或者在二进制文件或非常大的文件中找不到。PASS意思是这些规则什么都没发现,不是说什么都没有。旋转任何已提交的键。 - GitHub自己的缓存无法看到。 如果存储库曾经推送过敏感内容,则副本可能存在于此工具无法检查的地方(fork、pull-request refs、缓存视图)。安全的答案是一个没有历史记录的新存储库,该工具会这样说,而不是声称重写就足够了。
- 修改、重新定位、暂存和存放旧的对象。 它们不再在任何分支上,而是留在存储库中,直到git首先清理
git reflog expire --expire-unreachable=now --all,然后git gc --prune=now; reflog保持已修改的和重新基于的提交活动)。它们是本地的,推送不会发布它们,所以它们只会发出警告。 - 读取示例env文件。
.env.example,.env.sample和.env.template像任何其他文件一样扫描秘密;其他.env文件按名称标记,并且永远不会打开。 - 它不是普通秘密扫描仪的替代品,它不提供任何法律或许可方面的建议。
贡献与安全
欢迎发布和pull请求;请阅读 行为准则。保持测试数据明显是假的(比如 zq-example),并且从不包含任何真实的内容。运行 pnpm check, pnpm test 和 pnpm build 在发送更改之前,提交重新构建的 skill/launch-guard.mjs 用它。
要报告漏洞,包括工具打印私有内容的任何情况,请参见 SECURITY.md.
执照
麻省理工学院。看到 许可证.