kuro-tomo/sinkseal
GitHub: kuro-tomo/sinkseal
一款为 AI 编程代理设计的 Git Hook 防护栏工具,在 commit 和 push 节点自动执行密钥扫描、风险标记和实弹 sink 检测,防止危险改动在大量 diff 中被遗漏。
Stars: 0 | Forks: 0
# SinkSeal
[](https://github.com/kuro-tomo/sinkseal/actions/workflows/ci.yml)
[](LICENSE)
为 AI 编程代理设计的 Git-hook 防护栏。无论代码是由哪个代理编写的——Claude Code、Cursor、Codex 还是人类——它都能正常工作,因为控制逻辑存在于 git hook 中,而不是依赖于任何单一工具的配置。
请阅读 [English](#english) 或 [日本語](#japanese)。
## 英语
### 为什么需要这个工具
AI 编程代理工作速度极快,并会生成大量的 diff。一旦代理开始针对真实的基础设施进行实际工作,两种故障模式就会反复出现:
1. **常规风险遗漏。** 机密信息混入提交中、迁移未经审查便发布、认证代码路径在无人标记的情况下被更改——这并非因为有人粗心大意,而是因为代理生成的 diff 数量庞大,导致 risky 的改动很容易混入常规改动之中。
2. **实弹演习事故。** 演示或测试代码路径直接调用了*真实的*外部服务——例如 SMS API、支付 API、公开发布 API——而且因为“这只是一个演示”,没有人对其设置防护。一个有记录的事件是:某产品的交互式演示(点击 Logo,按下“测试通知”按钮)直接连接到了真实的 SMS/语音供应商,导致一次常规的演示产生了真实的电话扣费。该演示代码并不包含恶意,阅读起来甚至也没有明显的错误——它只是调用了与生产环境相同的函数,且没有任何防护层来检查调用者是否为演示环境。
仅靠 diff 审查无法可靠地捕捉到这两种情况,原因有二。首先,基于“更改的行”来判定风险,会遗漏在任何人开始关注它之前就已经存在于 main 分支上的调用点。其次,“阅读整个函数”这种审查指令在面对庞大的数量时会失效——它依赖于人类每次都能记得去执行,而这正是应该由机器检查来取代的检查清单依赖。
SinkSeal 是一套 git hooks,它以机械化的方式实现了这两种检查,因此无论是由哪个代理(或人类)编写了更改,也无论是否有人记得要求进行审查,它们都会在每次 commit 和每次 push 时运行。
### 包含内容
```
hooks/
pre-commit gitleaks (staged diff) + R-flag scan + live-fire
sink scan + open-remediation cross-check
commit-msg requires an explicit trailer when pre-commit
raised a flag (no silent pass-through)
pre-push runs typecheck/tests on push to the default
branch; optional same-day changelog reminder
rflag-scan.sh pattern-matching engine for risk-flag rules
sink-scan.sh live-fire sink scan for the staged diff (the
"flow" check — catches new sink usage)
no-live-fire-check.sh full-tree live-fire sweep for CI (the "stock"
check — catches sinks that predate this tool)
sink-inventory-drift.sh ledger-coverage check: flags a live-fire vendor
present in your dependencies with no matching
sink pattern
check-open-remediations.sh Claude Code-specific: cross-checks a change
against open items ("- [ ] ") in that project's
memory notes; a no-op everywhere else
lib/
rflag-rules-default.txt default risk-flag patterns (PATH: / CONTENT:)
sinks-default.txt default live-fire sink patterns
sink-hosts.txt vendor API hostnames (catches raw-HTTP bypass
of an SDK)
live-fire-vendors.txt vendor table used by the ledger-coverage check
templates/
LIVE_FIRE.md registration template for an intentional
live-fire demo path
skills/
setup.md a Claude Code skill that installs SinkSeal end
to end (run install.sh, wire CI, walk through
the first commit) — drop into ~/.claude/skills/
or the Anthropic Skills directory
tests/
smoke_test.sh end-to-end regression suite (throwaway repo,
installs the kit, asserts exit codes)
pattern_complexity_test.sh static ReDoS guard for lib/*.txt (flags a
nested-quantifier shape before it ships)
install.sh installs everything into a target repo,
self-contained (no dependency on this checkout
continuing to exist)
```
### 核心理念:sink sealing
“sink”是指将函数调用转化为对外部世界不可逆影响的最后一行代码——例如真正发送的 SMS、真正扣除的款项、真正发布的帖子。sink 的调用者在整个代码库中会不断倍增;但 sink 本身的数量却很少。因此,防护应该**加在 sink 上**,而不是加在每一个调用者上:通过一个受保护的 relay(例如 `notifier.notify()`),对于合成/测试/演示用的标识符无条件执行 no-op,而不是寄希望于每个调用点都能记得先进行检查。
SinkSeal 在两个节点上强制执行此操作:
- **Flow**(`sink-scan.sh`,在每次 commit 时运行):如果 demo/mock 路径添加了*原始的* sink 调用——绕过了受保护的 relay——这属于硬性拦截(hard block)。任何其他触及原始 sink 的路径都会产生一个软性标记,需要人工确认。看起来像测试的路径(`test`、`spec`、`e2e`、`__tests__`、`.stories.`)被视为安全网而不是危险源,因此被排除在这两项检查之外——纯粹在测试文件中添加的原始 sink 调用既不会被拦截,也不会被标记。
- **Stock**(`no-live-fire-check.sh`,在 CI 中运行):一次针对全树的扫描,因为 flow 检查只能看到新的 diff。在此工具安装之前就已经进入 main 分支的代码,需要进行专门的扫描。
第三种检查 `sink-inventory-drift.sh`,用于捕捉前两种检查都无法应对的故障模式:你的项目添加了一个新的 live-fire 供应商(例如,从 Twilio 切换到 Vonage),但没有人向 sink 表中添加匹配的模式。随后,扫描就会对该供应商“静默失明”,同时继续报告“通过”。此检查会将你的依赖清单作为“实际存在的内容”的唯一事实来源,如果账本未及时更新,则会导致检查失败。
有意的 live-fire 路径(需要拨打真实电话才能完成的真实销售演示)不会被禁止——它会在 `LIVE_FIRE.md` 中注册为例外,并被添加到 `.forge/sinks-allow.txt` 中。请参阅该模板以了解它要求填写的字段,特别是“disabled”一项,必须具体说明*哪一个防护层*失效了,而不仅仅是写上“disabled”这个词。
### 前置条件
- 必须在 `PATH` 中安装 `gitleaks`,以便 `pre-commit` 中的机密扫描能够实际运行。如果未安装,`pre-commit` 会打印一条警告并继续执行,但*不会*扫描机密——它不会拦截提交,除了那一行警告外也不会产生明显的报错。在依赖此检查之前,请从 https://github.com/gitleaks/gitleaks 进行安装。
### 安装说明
```
./install.sh /path/to/your/repo
```
这会将 hooks 以独立打包的形式复制到 `/.githooks/` 并相应地设置 `core.hooksPath`。因为复制后的内容是独立的,所以即使这个 SinkSeal 检出(checkout)后来被删除了,它也能继续工作,并且该仓库的任何 git worktree 都会自动继承它(worktree 共享被追踪的文件)。
`install.sh` 还会验证 `no-live-fire-check.sh` 和 `sink-inventory-drift.sh` 是否确实在你的 CI 配置(GitHub Actions、GitLab CI、Bitbucket Pipelines 或 CircleCI)中被引用了——不仅是已安装,而且是被接入了 CI——如果没有则返回非零退出码。一个 CI 从未运行过的 hook 提供不了任何保护,而一个无论结果如何都报告成功的安装脚本,只会让所有人去盲目信任一个实际上从未被接入的安装过程。
**更新工具包。** `install.sh` 只负责复制文件;它不会留下指回此检出(checkout)的链接。如果你拉取了较新的 SinkSeal 并希望应用更改,请针对你安装了该工具的每个仓库重新运行 `install.sh`——不存在自动传播机制。
### 按项目调优
- `.forge/rflag-rules.txt` — 额外的风险标记规则(格式同 `PATH:`/`CONTENT:`)
- `.forge/sinks.txt` — 额外的 sink 模式
- `.forge/sinks-allow.txt` — 合法持有 sink 的文件白名单(由 CI 扫描读取)
- `.forge/vendors.txt` — 用于账本覆盖检查的额外 live-fire 供应商(格式同 `vendor;;presence;;coverage;;category`,与 `lib/live-fire-vendors.txt` 相同)
- `.forge/vendors-allow.txt` — 在此项目中被判定为不属于 live-fire 的供应商,附带原因
- `.forge/gates.sh` — 项目特定的测试/类型检查命令(需设为可执行)— 覆盖 `pre-push` 中的通用自动检测
- `.forge/changelog-path.txt` — 可选:指向 changelog 文件的仓库相对路径;如果其中没有包含今天日期的条目,`pre-push` 会发出警告(非阻塞)
你添加到 `.forge/rflag-rules.txt` 或 `.forge/sinks.txt` 中的每个模式都会在每次 commit 和 CI 运行时直接输入到 `grep -E` 中,除了检查正则表达式的语法是否有效之外,没有任何复杂性验证。病态的模式(例如包含重复量词的组中又嵌套了重复量词,如 `(a+)+`)可能会导致该仓库未来的每一次提交都出现性能退化。`tests/pattern_complexity_test.sh` 会在该工具包自带的 `lib/*.txt` 发布之前,对这种结构的模式进行静态标记——在合并你自己对 `.forge/*.txt` 的添加内容之前,请务必复制相同的检查机制进行核对。
### 关于绕过
`git commit --no-verify` / `git push --no-verify` 会完全跳过这些 hooks——这就是 git hooks 的工作机制,SinkSeal 也不打算掩盖这一点。它所提供的是一种由机器强制执行的默认行为:除非有人明确选择退出,否则每次 commit 和 push 都会受到检查,这与“只有在有人记得要求时才进行检查”的姿态截然不同。如果你专门使用 Claude Code,你还可以在自己的环境中额外配置一个 `PreToolUse` hook,以便在工具层拦截 `--no-verify` 调用——这属于个人或组织层面的策略选择,超出了本仓库提供的范围,因为这取决于你自己的代理工具配置。
### 许可证
MIT。详见 `LICENSE`。
## 日本語
### これは何か・なぜ作ったか
AIコーディングエージェントは大量の差分を高速に生成する。実際のインフラに対して
本物の作業をさせ始めると、以下の二つの見落としが繰り返し発生する。
1. **通常のリスク見落とし。** 誰かが不注意だったからではなく、大量の差分の中に
危険な一件が紛れ込みやすくなるために、秘密情報の混入・未レビューのマイグレーション・
認証経路の変更などが素通りしてしまう。
2. **実弾事故。** デモ/テスト用のコード経路が「本物の」外部サービス(SMS API・
決済API・公開投稿API等)を直接呼び出してしまい、「デモに過ぎぬ」という思い込みから
誰も歯止めをかけていない。実例:ある製品の対話デモ(ロゴのダブルクリック・
「通知テスト」ボタンの押下)が実際のSMS/音声ベンダーへ直結しており、通常のデモ
操作が実際の電話料金を発生させた。デモコード自体は悪意もなく、読んで明白に
おかしいわけでもない——本番と同じ関数を、呼び出し元がデモか否かを確認せぬまま
呼んでいただけである。
差分レビューだけではこの両方を確実には捕捉できない。第一に、「変更行のみ」を見る
リスク判定は、統制が敷かれる以前からmainに存在していた呼び出し箇所を見逃す。
第二に、「関数全文を読め」という指示は量が増えるほど劣化する——毎回人間が覚えて
実行することに依存しており、それこそがチェックリストを機械照合へ置き換えるべき
典型例である。
SinkSealは、この両方の検査を機械的に組み込んだGit hook一式である。どの
エージェント(あるいは人間)が変更を書いたかに関わらず、また誰かがレビューを
思い出したか否かに関わらず、コミット・プッシュの都度作動する。
### 構成
`hooks/`(pre-commit・commit-msg・pre-push・rflag-scan.sh・sink-scan.sh・
no-live-fire-check.sh・sink-inventory-drift.sh・check-open-remediations.sh)、
`lib/`(既定のR旗規則・砲口パターン・ベンダー表)、`templates/LIVE_FIRE.md`
(意図的な実弾デモの登録雛形)、`install.sh`(導入スクリプト)。詳細は上の
英語セクションの構成図を参照(コメントも含め内容は同一)。
### 核心思想:砲口封印
「砲口」とは、関数呼び出しを外部世界における不可逆な作用——実際に送信
されたSMS、実際に確定した決済、実際に公開された投稿——に変える最後の呼び出し
箇所である。砲口の呼び出し元はコードベース中に増殖するが、砲口自体は少数に
留まる。ゆえにガードは全ての呼び出し元でなく**砲口自身**に置く:合成/テスト/
デモ用の識別子に対して無条件でno-opするガード付き中継(例:`notifier.notify()`)
を経由させ、各呼び出し元が確認を怠らぬことに期待しない。
SinkSealはこれを二点で強制する——**flow**(`sink-scan.sh`・毎コミット。デモ/
モック領域からの生砲口追加はハードブロック、その他は軟R旗)と、**stock**
(`no-live-fire-check.sh`・CI必須。統制施行前からmainに眠る経路を全木走査で
掃討)。三つ目の`sink-inventory-drift.sh`は、新しい実弾ベンダーを導入した際に
砲口パターンの追加を忘れる、という前二者では捕捉できぬ化を検知する。
意図的な実弾デモ(本物の営業実演等)は禁止でなく、`LIVE_FIRE.md`への登録
例外として統制下に置く。「無効化済み」の一語で済ませず、どの層が生きて
どの層が落ちているかを具体的に記す欄が要る。
### 導入
```
./install.sh /path/to/your/repo
```
対象リポジトリの`.githooks/`へ自己完結の形で複製し、`core.hooksPath`を
設定する。自己完結ゆえこのSinkSeal自体を後で削除しても動作し、worktreeにも
自動継承される。`install.sh`は`no-live-fire-check.sh`・`sink-inventory-drift.sh`
が実際にCI設定(GitHub Actions・GitLab CI・Bitbucket Pipelines・CircleCIのいずれか)
から呼ばれているかを機械検証し、未配線ならexit 2で知らせる——「導入した」と
「CIで実際に動いている」は別物であるため。
**キット更新時:** `install.sh`はファイルを複製するのみで、この配布元への
参照を残さぬ。新しいSinkSealを取得した際に反映させるには、導入済みの各
リポジトリへ`install.sh`を再実行する要あり——自動的な伝播はせぬ。
### 案件固有の調律
`.forge/rflag-rules.txt`・`.forge/sinks.txt`・`.forge/sinks-allow.txt`・
`.forge/vendors-allow.txt`・`.forge/gates.sh`・`.forge/changelog-path.txt`
(詳細は英語セクション参照)。
### 迂回について
`--no-verify`はhookそのものを迂回する——これはgit hookの仕様であり、隠す
つもりはない。本ツールが提供するのは「誰も明示的に外さぬ限り、全コミット・
全プッシュが検査される」という既定動作であり、「誰かが思い出した時だけ
検査される」姿勢とは質的に異なる。Claude Codeを使う場合、自環境で
`PreToolUse` hookを設け`--no-verify`自体をツール層で拒否することも可能だが、
それは各自のエージェント運用に依存する個人/組織側の方針であり、本リポジトリ
が同梱する範囲を超える。
### SinkSeal Pro Kit
金融(PCI関連)・医療(HIPAA関連)・EU GDPR等、規制産業向けの拡張R旗パターン、
及びこのキットのbranch protection運用と対になる`/done`・`/ship`のClaude Code
スキルは、有料の**SinkSeal Pro Kit**として別途Gumroadにて提供予定にて候
(本ページの内容はいずれも無料のまま)。
### 许可证
MIT。`LICENSE`を参照。
标签:AI编程助手, Git-hooks, StruQ, 代码安全审查, 错误基检测, 静态代码分析, 风险控制