创新方案
用 Conventional Commits…
当用户说「提交」「git commit」「记录变更」时,从 staged 差分推断 type/scope、先做两段式密钥检查、生成规范提交信息并验证结果。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 Conventional Commi通用
解决思路
- Step 1 确认变更:先执行 `git status` 和 `git diff --staged`;若没有 staged 变更,再执行 `git diff` 查看内容,并引导用户先做 staging。
- Step 2 密钥混入检查(必须执行,不可跳过):采用 (a) 机械比对 + (b) 目视审查两段式;(a) 单独不够,(b) 即使 (a) 无任何检测结果也必做。
- (a) 机械比对:把正则模式只在变量里定义一次,自测与本检查共用同一字符串。因为 BSD grep / GNU grep / ugrep 对 BRE 的 `\+` 解释不同,可能报语法错误;出错的 grep 以非零退出,`... || echo "检测なし"` 的写法会在存在密钥时也报「未检测到」(fail-open)。
- (a) 排除侧(`grep -vE`)同样有该风险:排除模式坏掉会让已匹配的行消失而变成「未检测到」,所以自测必须把排除侧也一起跑。
- (a) 不要把「模式定义」和「检查」拆到不同代码块:代码块可能各自在独立 shell 中执行导致变量不继承,且会留下「未被测试过的正则」。必须写在同一个块内完成。
- (a) 定义凭据文件名模式:`RE_CREDFILE='(^|/)\.env(\..+)?$|(^|/)(id_rsa|id_ed25519)(\..+)?$|\.(pem|p12|pfx|key)$'`,以及模板/公钥排除模式 `RE_CREDFILE_OK='\.(example|sample|template|pub)$'`。
- (a) 定义高信頼模式 `RE_HIGH`:厂商固有 token 形式(`sk-`、`ghp_`、`github_pat_`、`AKIA`、`xox[baprs]-`、`AIza`)、私钥头 `BEGIN (RSA |EC |OPENSSH |PGP )?PRIVATE KEY`、三段式 JWT(`eyJ...eyJ...`)、内嵌凭据的连接串(`scheme://user:pass@host`)。
- (a) 定义赋值形模式 `RE_ASSIGN='(password|passwd|secret|client_secret|api_?key|[a-z_]*token|aws_secret_access_key|private_?key)[[:space:]]*[:=][[:space:]]*[^[:space:]]{8,}'`:token 系不限定前缀(`-i` 配合可一次抓到 GITHUB_TOKEN / GH_TOKEN / access_token 等),逐个列键名必然漏检,误报交给 (b) 处理。
- (a) 定义赋值形排除模式 `RE_ASSIGN_OK`:`${`、`$(`、`%VAR%`、`process.env`、`os.environ`、`ENV[`、`getenv`、`<PLACEHOLDER>`、`***`、REDACTED、CHANGEME、placeholder、dummy、xxxx、example.com、your-password/secret/key/token 等环境变量引用与占位符。
- (a) 执行自测:验证「实际值能通过检测」「排除侧不会误杀实际值」「高信頼模式能命中连接串与 JWT」「`.env` 命中而 `.env.example` 被排除」「`${DB_PASSWORD}` 被排除」等断言;任一项失败就输出错误并 `exit 1` 中止提交。排除侧的断言要用「实际值存活」的方向写,否则排除模式的语法错误会与「已排除」无法区分,导致测试本身 fail-open。
- (a) 检查 1:用 `git diff --staged --name-only --diff-filter=ACMR | grep -E "${RE_CREDFILE}" | grep -vE "${RE_CREDFILE_OK}"` 检测凭据文件;只看新增/修改,不阻止「删除泄露 .env」这类整改提交。
- (a) 检查 2:用 `git diff --staged --diff-filter=ACMR | grep -E '^\+' | grep -vE '^\+\+\+' | grep -E "${RE_HIGH}"` 检测高信頼模式;只针对新增行,避免妨碍删除已泄露密钥。
- (a) 检查 3:对同样的新增行跑 `grep -iE "${RE_ASSIGN}" | grep -vE "${RE_ASSIGN_OK}"` 检测赋值形;该模式误报率高,不自动中止,而是把命中行提示给用户确认。
- (a) 检查 4:用 `git diff --staged --name-status --diff-filter=R | cut -f2 | grep -E "${RE_CREDFILE}" | grep -vE "${RE_CREDFILE_OK}"` 检测凭据文件重命名(如 `.env` → `config.txt`);因为上面的名字匹配只看重命名后的新路径,而 `.env` 的 KEY=VALUE 内容往往不匹配高信頼模式,名字匹配才是主防线。
- 处理 1/2/4 的命中:中止提交并警告用户,指导其 unstage 该文件或删除对应行;只有能判定为例示值/占位符等误报时,在取得用户明确确认后才可继续。
- 处理 3 的命中:不要中止,先把该行展示给用户确认是否为真实值;若确认是实值则中止提交。
- 处理 4 的命中:确认密钥内容是否仍残留在新文件名下,若仍在则中止。仅含删除的提交(删除 `.env`、移除已泄露密钥)不属于检测对象,可直接继续;但同一提交里同时含凭据文件的删除(D)与其他文件名的添加(A)时,要先看 diff 确认不是把内容换个名字重新加进来的实质重命名。
- (b) 目视审查(机械比对报「未检测到」时也必做):执行 `git diff --staged --diff-filter=ACMR | grep -E '^\+' | grep -vE '^\+\+\+'`,只读新增行,按 5 个观点判断:任意命名的凭据(`dbPass`、`clientKey`、`token2` 等不在键名列表里的变量名);结构化文件中的实值(`config.yml` / `settings.json` / `*.tf` / k8s manifest 里直写的密码);高熵字符串(用途不明的长随机 base64/hex);连接信息片段(主机名、端口、用户名与值成套出现);二进制或已编码内容(如 k8s Secret 的 `data:` 里 base64 编码的凭据)。
- (b) 判定:若认为是密钥或无法判断,中止提交并向用户展示该处确认。
- 明确 (a) 的契约:它是模式匹配、并非穷尽式检测。原理上无法发现「任意变量名承载的凭据」「无格式规律的自研 token / DB 密码」「恰好命中检查 3 排除模式的实值」。因此 Step 2 的契约不是「(a) 没检出就安全」,而是「(a) 机械落掉明显的,(b) 由人判断剩下的」。组织启用了 GitHub secret scanning / push protection 也只是 push 时点的额外防线(本地提交时不生效),不能替代本流程。
- Step 3 决定 Conventional Commits 的 type:`feat` 新功能、`fix` 修 bug、`docs` 仅文档、`refactor` 不改行为的重构、`test` 测试增改、`chore` 构建流程/辅助工具变更、`style` 仅代码风格(空白、格式化)、`build` 构建系统或外部依赖变更、`ci` CI 配置变更、`perf` 性能改善。
- Step 4 从变更文件路径推断 scope;跨多个 scope 时可省略。
- Step 5 生成 `type(scope): subject` 形式的提交信息(如 `feat(auth): ソーシャルログイン機能を追加`、`fix(api): レスポンスのエラーハンドリングを修正`、`refactor(ui): コンポーネント構造を整理`),先提示给用户并取得确认。
- Step 6 执行提交:`git commit -m "$(cat <<'EOF' … EOF)"`,按需带上 `Co-Authored-By` 等尾部信息。绝对禁止使用 `--no-verify`(不得绕过 pre-commit hook)。
- Step 7 验证:执行 `git log --oneline -3` 确认最新提交信息符合 `type(scope): subject` 格式,并用 `git show --stat HEAD` 确认变更文件列表符合预期。
- 常见失败规避:多个关注点混在一个提交里 → 按关注点用 `git add -p` 分开 staging、分成多个提交;type 选错(修 bug 用 `feat`、加功能用 `fix`)→ 对照 Step 3 的 type 表,以差分的意图为准选择。
- 常见失败规避:跳过密钥检查直接进入 type 决定 → Step 2 不可省略,检出时中止提交并警告用户;试图用 `--no-verify` 绕过 hook → 排查并修好 hook 失败原因后重新提交,禁止绕过。
- 常见失败规避:subject 超过 72 字符 → 省略 scope 或缩短 subject,细节写进 body。
- 注意事项:有 breaking change 时加 `!`(`feat!: ...`)或在 body 写 `BREAKING CHANGE:`;subject 用命令式/现在式书写(可用日文);subject 控制在 72 字符内;包含 `.env` 或凭据文件时必须警告并中止提交(由 Step 2 保证检出)。
适用边界 · 注意事项
- 仅删除凭据文件的整改提交(删除 `.env`、移除已泄露密钥)本就不应被旧的阻止逻辑拦住;但同一提交里同时有凭据文件删除(D)与其他文件名添加(A)时,必须先确认不是在换名重加。
- 不能把 (a) 机械比对的「未检测到」当作安全证明,也不能用组织侧的 GitHub secret scanning / push protection 替代本流程(本地 commit 时点不生效)。
- 尚未做 staging 时不能直接提交,需先查看 `git diff` 并引导用户 staging。