本文目录导读:

- 检查提交信息中是否包含审计标识(最常见)
- 检查被修改的文件是否符合审计规则(pre-commit)
- 检查是否符合代码签名或 GPG 签名(pre-commit / commit-msg)
- 高级:检查外部审计数据库(pre-commit 或 pre-push)
- 重要注意事项
Git 钩子脚本可以通过多种方式检查审计跟踪(Audit Trail),但审计跟踪的数据来源决定了具体的检查方法,审计跟踪可以存在于代码本身、提交信息(Commit Message)、文件变更清单或外部系统。
以下是几种核心的检查策略及其对应的钩子脚本示例(以 pre-commit 或 commit-msg 钩子为例,使用 Shell 脚本):
检查提交信息中是否包含审计标识(最常见)
许多审计要求规定,每次提交必须包含一个 Issue ID、Jira Ticket、Bug ID 或 Change Request (CR) 编号,以便将代码变更与审计事件关联起来。
- 钩子类型:
commit-msg(在提交信息写入后触发) - :确保提交信息匹配
ABC-123或[CR-456]等正则表达式。
脚本示例(commit-msg):
#!/bin/sh
# .git/hooks/commit-msg
# 检查提交信息中是否包含审计跟踪ID ( A-123 或 Feature/123)
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
# 正则表达式:匹配 JIRA 风格 ID 或特定前缀
PATTERN="([A-Z]+-[0-9]+|CR-[0-9]+|Issue #[0-9]+)"
if echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
exit 0 # 通过
else
echo "错误:提交信息必须包含审计跟踪 ID(PROJ-123、CR-456 或 Issue #789)。"
echo "当前提交信息: $COMMIT_MSG"
exit 1 # 阻止提交
fi
检查被修改的文件是否符合审计规则(pre-commit)
有些审计要求某些敏感文件(如配置文件、环境变量、密钥文件)的修改必须经过特殊处理(例如不允许直接提交,或必须附带签名)。
- 钩子类型:
pre-commit(在提交前触发) - :扫描
git diff --cached中准备提交的文件路径。
脚本示例(pre-commit):
#!/bin/sh
# .git/hooks/pre-commit
# 检查是否试图提交审计跟踪中不允许直接修改的文件
# 获取暂存区中所有被修改、新增的文件列表(排除删除的文件)
FILES=$(git diff --cached --name-only --diff-filter=ACM)
# 定义不允许直接提交的审计敏感文件模式
SENSITIVE_PATTERNS=".*\.pem$|.*secret\.yaml$|config\/production\.env|\.htpasswd"
# 检查是否存在匹配模式的文件
MATCHING_FILES=$(echo "$FILES" | grep -E "$SENSITIVE_PATTERNS" || true)
if [ -n "$MATCHING_FILES" ]; then
echo "错误:审计跟踪禁止直接提交以下敏感文件:"
echo "$MATCHING_FILES"
echo "请通过审计批准的流程(如 Vault 或加密通道)处理这些文件。"
exit 1
fi
# 另一个检查:确保没有文件中包含特定的审计标记(如 TODO_AUDIT)
if git diff --cached -p | grep -q "^\+.*TODO_AUDIT"; then
echo "错误:提交中包含未解决的审计跟踪标记(TODO_AUDIT),请先完成审计事项。"
exit 1
fi
exit 0
检查是否符合代码签名或 GPG 签名(pre-commit / commit-msg)
审计通常要求提交不能被篡改,并且身份可验证,虽然钩子本身不能直接强制 GPG 签名,但可以检查暂存区内容是否满足某些“签署”条件。
- 钩子类型:
pre-commit - :验证用户是否有权限提交,或者是否在提交信息中包含了数字签名哈希。
脚本示例(pre-commit):
#!/bin/sh
# 检查提交者的邮箱是否属于审计允许的域
if ! echo "$GIT_AUTHOR_EMAIL" | grep -qE "@company\.com$"; then
echo "审计失败:提交者邮箱 '$GIT_AUTHOR_EMAIL' 不在审计允许的域内。"
exit 1
fi
# 检查是否包含验证签名(例如提交信息中包含一个由外部工具生成的签名)
# 这适合替代方案,如果不使用 GPG
# COMMIT_MSG=$(cat "$1")
# if echo "$COMMIT_MSG" | grep -q "^Signed-off-by:"; then
# echo "审计禁止使用 Signed-off-by,请使用 GPG 签名。"
# exit 1
# fi
高级:检查外部审计数据库(pre-commit 或 pre-push)
对于严格的审计环境,可以编写脚本在提交时查询外部 API(如 Jira、ServiceNow、内部审计 API),验证 Jira Ticket 状态是否为“已批准”或“已审计”。
- 钩子类型:
pre-push(推送前检查,防止不符合审计的代码进入远程仓库) - :解析提交范围,提取 Ticket ID,调用 REST API。
脚本示例(pre-push,伪代码):
#!/bin/sh
# .git/hooks/pre-push
# 在推送前检查所有即将推送的提交是否关联了已批准的审计工单
z40=0000000000000000000000000000000000000000
while read local_ref local_sha remote_ref remote_sha; do
# 获取要推送的提交列表
for commit in $(git rev-list ${remote_sha:-$z40}..$local_sha); do
COMMIT_MSG=$(git log --format=%B -n 1 $commit)
TICKET_ID=$(echo "$COMMIT_MSG" | grep -oP '[A-Z]+-\d+')
if [ -z "$TICKET_ID" ]; then
echo "审计失败:提交 $commit 缺少审计 Ticket ID。"
exit 1
fi
# 调用外部审计 API 检查状态(示例:curl)
# RESPONSE=$(curl -s "https://audit.company.com/api/tickets/$TICKET_ID")
# STATUS=$(echo "$RESPONSE" | jq -r '.status')
# if [ "$STATUS" != "APPROVED" ]; then
# echo "审计失败:Ticket $TICKET_ID 状态为 $STATUS,需要 'APPROVED'。"
# exit 1
# fi
echo "提交 $commit 关联的 $TICKET_ID 审计通过。"
done
done
exit 0
重要注意事项
- 钩子可以被绕过:
git commit --no-verify(或-n)会跳过所有钩子。基于客户端的钩子不能作为审计的最终防线,它只能作为开发者的便利性检查。 - 真正的审计必须在服务器端强制执行:要实现可靠的审计跟踪检查,必须在远程仓库(如 GitHub、GitLab、Bitbucket) 的服务端钩子(如
pre-receive)或受保护的推送规则中实现,客户端钩子只是第一道防线。 - 脚本性能:如果检查外部 API,每次提交都发起 HTTP 请求会显著降低速度,需考虑缓存或批量检查。
- 跨平台兼容:上述脚本假设是 Linux/macOS 环境,如果是 Windows,可以使用 Git Bash 或 PowerShell 实现类似逻辑。
| 审计检查内容 | 推荐钩子类型 | 核心逻辑 |
|---|---|---|
| 提交信息包含 Ticket ID | commit-msg |
正则匹配提交信息 |
| 禁止提交敏感文件 | pre-commit |
git diff --cached 扫描文件路径 |
| 提交者身份/域名 | pre-commit |
检查 $GIT_AUTHOR_EMAIL 或 $GIT_COMMITTER_NAME |
| 关联外部系统状态 | pre-push |
解析提交范围,调用外部 API |
| 代码签名/签名检查 | pre-commit |
检查 GPG 是否启用或提交信息是否包含签名标记 |