平时我们写代码的时候难免会有一些私密信息不行提交到git仓库上去,比如 账号,密码,AppId, key 之类不希望公开的信息,但是提交代码难免会有疏漏的时候,对此我们可以写个 githook
来协助我们进行检查。
目的
在Git提交操作前,对即将提交的文件进行全面扫描,检查其中是否存在预设的敏感信息,防止敏感数据不慎进入版本控制系统,有效维护项目的安全性与保密性。
比如我们现在这么一段代码
const mysql = require("mysql");
const pool = mysql.createPool({host: "jyeontuHost",user: "jyeontuUser",password: "jyeontuPassword",database: "jyeontuDatabase",port: 3306,multipleStatements: true, // 多语句查询
});
假设其中的用户名(jyeontuUser) 和密码(jyeontuPassword) 是敏感信息,我们不想这两个信息直接提交到git仓库,所以希望在提交代码的时候可以检测一下是不是包含敏感信息,包含敏感信息的话就中断commit 通知我们进行整改,整改后再重新提交。
效果展示
commit的时候检测到提交的文件中包含有敏感信息时,会中断commit,并在控制台打印包含敏感信息的文件路径及行号,可以帮助我们快速定位到敏感信息的位置。
代码实现
1、指定脚本解释器
作为一名前端开发,我们可以使用node 来写一个githook
#!/usr/bin/env node
2、引入必要模块
const child_process = require("child_process");
const fs = require("fs");
这里引入了child_process模块,用于在Node.js环境中执行外部命令,如git status
;fs模块则提供了一系列与文件系统交互的方法,包括读取文件内容、检查文件是否存在等操作,是后续处理文件的关键。
3、获取Git状态信息
const command = "git status";
const trimReg = /(\ +)|([ ])|([\r\n]|(["]))/g;
let commitFile = child_process.execSync(command).toString();
commitFile = commitFile.split("\n").filter((item) => item);
首先定义了要执行的git status命令字符串,然后通过execSync同步执行该命令,并将输出转换为字符串存储在commitFile中。接着,按换行符将其分割为数组并过滤掉空行,得到一个更清晰的文件状态信息数组,为后续解析做准备。
4、解析文件列表
const fileList = [];
let index = 0;
while (commitFile[index]!== "Changes to be committed:" &&index < commitFile.length
)index++;
for (let i = index; i < commitFile.length; i++) {const name = commitFile[i].trim().split(":")[1];if (!name) continue;if (name.includes("->")) {fileList.push(decodeURI(name.split("->")[1].replace(trimReg, "")));continue;}fileList.push(decodeURI(name.replace(trimReg, "")));
}
我们需要检查的是Changes not staged for commit状态的文件
从commitFile数组中提取出即将提交的文件列表。先通过while循环找到"Changes to be committed:"所在索引,确定要处理的文件信息起始位置。然后在for循环中,对每一行文件信息进行处理,提取文件名(处理了文件重命名情况),并使用decodeURI解码后添加到fileList数组,得到完整的待提交文件列表。
5、检查敏感信息
//敏感信息列表
const sensitiveInformationList = ["jyeontu的密码","jyeontu的AppId","jyeontu的账号",
];const editFiles = [];
for (const file of fileList) {if (!fs.existsSync(file)) continue;const fileTxt = fs.readFileSync(file, "utf8").split("\n");for (let textLine = 0; textLine < fileTxt.length; textLine++) {const txt = fileTxt[textLine];for (const sensitiveInformation of sensitiveInformationList) {if (txt.includes(sensitiveInformation)) {editFiles.push(`\x1b[31m${file}:${textLine + 1}\x1b[0m -> ${sensitiveInformation}`);}}}
}
需要我们先定义好敏感信息列表,包含常见的敏感数据关键词。然后通过嵌套循环,遍历文件列表中的每个文件。对于存在的文件,读取其内容并逐行检查,若某行包含敏感信息列表中的任何一项,则将包含文件名、行号(红色字体显示以突出)和敏感信息的格式化字符串添加到editFiles数组,记录所有存在敏感信息的位置。
6、处理检查结果
if (editFiles.length > 0) {console.log("请删除敏感信息后再次提交:");console.log(editFiles.join("\n"));process.exit(1);
}
process.exit(0);
最后根据editFiles数组长度判断是否发现敏感信息。若有敏感信息(editFiles.length > 0),则输出提示信息及详细敏感信息位置,以非零状态码退出脚本,阻止Git提交;若未发现(editFiles.length === 0),则正常退出脚本,允许Git提交。
钩子使用
1、钩子目录
找到当前项目更目录下的.git
目录,里面有一个hooks 目录,这里便是我们存放钩子函数的目录。
2、拉取钩子代码
目前该钩子的模板我已经集成到了我自己的cli 工具中,大家可以通过cli 快速拉取
安装cli
npm i -g jyeontu
拉取钩子模版
jyeontu add
拉取后可以看到这么一个文件
3、修改敏感信息列表
拉取完钩子模板后,我们需要将敏感列表修改成我们自己需要检测的内容:
改完保存之后再重新提交代码就会触发内容检测了。
更优做法
其实正常情况下我们不应该将这些配置信息分散写在各个文件中,这样不好管理,修改起来也麻烦,通常我们都会将配置信息抽离成一个配置文件。
1、抽离配置文件
比如上面用到的mysql 相关配置
2、配置.gitignore
整个文件都不想提交的话我们配置好.gitignore就行
serve/dbConfig.js
3、移除文件跟踪
当然,有的时候我们还是要上传一份config
配置文件模板到仓库,便于用户修改自己的配置信息,这种时候我们就不可以直接在.gitignore
里面忽略文件了。
我们可以先写一份不包含敏感信息的配置文件模板上传到仓库中,然后再将其移除版本控制,这样后面即使文件的实际内容可能已经发生了变化,Git 也会将其视为未更改状态
git update-index --assume-unchanged <file>
4、恢复文件跟踪
git update - index -- no - assume - unchanged <file>
配置模板修改的话,我们需要上传新的模板,这时候我们可以先恢复文件跟踪,将最新模板上传到仓库后再移除文件跟踪。
配置文件抽离 ➕ gitHook检查,这应该可以很大程度 避免把 账号,密码,AppId, key 之类私密信息 提交上去 了。
源码
文中涉及到的git hooks 模板 和jyeontu cli 都已开源,有兴趣的同学可以看看:
1、git hooks 模板库
https://gitee.com/zheng_yongtao/jyeontu-templates
2、jyeontu cli
https://gitee.com/zheng_yongtao/node-scripting-tool/tree/master/src/jyeontu
原创 JYeontu 前端也能这么有趣