Skip to content

Commit 244ce81

Browse files
added scripts/check-commit-message.sh
1 parent 78cac96 commit 244ce81

1 file changed

Lines changed: 171 additions & 0 deletions

File tree

scripts/check-commit-message.sh

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#!/usr/bin/env bash
2+
###############################################################################
3+
# check-commit-message.sh
4+
#
5+
# Pre-commit hook to detect secrets, credentials, and sensitive information
6+
# in commit messages. This prevents accidental exposure of sensitive data
7+
# in git history via commit messages.
8+
#
9+
# This script checks commit messages for:
10+
# - Passwords and credentials
11+
# - IP addresses (especially private/internal IPs)
12+
# - API keys and tokens
13+
# - Email addresses (may indicate sensitive accounts)
14+
# - High-entropy strings that may be secrets
15+
###############################################################################
16+
17+
set -euo pipefail
18+
19+
# Colors for output
20+
RED='\033[0;31m'
21+
YELLOW='\033[1;33m'
22+
GREEN='\033[0;32m'
23+
NC='\033[0m' # No Color
24+
25+
# Get commit message from COMMIT_EDITMSG or stdin
26+
COMMIT_MSG_FILE="${1:-${GIT_DIR:-.git}/COMMIT_EDITMSG}"
27+
28+
# For commit-msg hook, pre-commit passes the file as first argument
29+
# For direct testing, check if stdin has data first
30+
if [[ -t 0 ]]; then
31+
# stdin is a terminal, not piped input - read from file
32+
if [[ -f "${COMMIT_MSG_FILE}" ]]; then
33+
COMMIT_MSG=$(cat "${COMMIT_MSG_FILE}" 2> /dev/null || echo "")
34+
else
35+
# Try to get from git (for pre-commit hook)
36+
if command -v git > /dev/null 2>&1; then
37+
COMMIT_MSG=$(git log -1 --pretty=%B 2> /dev/null || echo "")
38+
else
39+
COMMIT_MSG=""
40+
fi
41+
fi
42+
else
43+
# stdin has data (piped input) - read from stdin for testing
44+
COMMIT_MSG=$(cat 2> /dev/null || echo "")
45+
fi
46+
47+
# If still empty, exit silently (may be called in wrong context)
48+
if [[ -z "${COMMIT_MSG}" ]]; then
49+
exit 0
50+
fi
51+
52+
# Patterns to detect in commit messages
53+
# IP addresses (especially private IP ranges)
54+
# Using [0-9] instead of \d for POSIX grep compatibility
55+
# Word boundaries removed as they can be unreliable with IPs
56+
IP_PATTERN='(10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|172\.(1[6-9]|2[0-9]|3[01])\.[0-9]{1,3}\.[0-9]{1,3}|192\.168\.[0-9]{1,3}\.[0-9]{1,3})'
57+
58+
# Common password patterns (words that often appear with passwords)
59+
PASSWORD_PATTERN='\b(password|passwd|pwd|secret|credential|token|key)\s*[:=]\s*[^\s]{8,}'
60+
61+
# High-entropy strings (potential secrets)
62+
HIGH_ENTROPY_PATTERN='\b[a-zA-Z0-9+/=]{32,}\b'
63+
64+
# Known credential patterns
65+
CREDENTIAL_PATTERN='\b(api[_-]?key|access[_-]?token|secret[_-]?key|auth[_-]?token)\s*[:=]\s*[^\s]{16,}'
66+
67+
# Email addresses (may indicate sensitive accounts)
68+
EMAIL_PATTERN='\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
69+
70+
# Function to check entropy (simplified)
71+
calculate_entropy() {
72+
local str="$1"
73+
local len=${#str}
74+
if [[ ${len} -lt 16 ]]; then
75+
echo "0"
76+
return
77+
fi
78+
79+
declare -A chars
80+
local i
81+
for ((i = 0; i < len; i++)); do
82+
chars[${str:i:1}]=1
83+
done
84+
echo "${#chars[@]}"
85+
}
86+
87+
# Function to check for sensitive patterns
88+
check_commit_message() {
89+
local found_issues=0
90+
local issues=()
91+
92+
# Check for IP addresses
93+
if echo "${COMMIT_MSG}" | grep -qE "${IP_PATTERN}"; then
94+
local ips
95+
ips=$(echo "${COMMIT_MSG}" | grep -oE "${IP_PATTERN}" | sort -u | head -5)
96+
issues+=("IP addresses detected: ${ips}")
97+
found_issues=$((found_issues + 1))
98+
fi
99+
100+
# Check for password patterns
101+
if echo "${COMMIT_MSG}" | grep -qiE "${PASSWORD_PATTERN}"; then
102+
issues+=("Password or credential pattern detected")
103+
found_issues=$((found_issues + 1))
104+
fi
105+
106+
# Check for credential patterns
107+
if echo "${COMMIT_MSG}" | grep -qiE "${CREDENTIAL_PATTERN}"; then
108+
issues+=("API key or token pattern detected")
109+
found_issues=$((found_issues + 1))
110+
fi
111+
112+
# Check for high-entropy strings (potential secrets)
113+
local high_entropy_matches
114+
high_entropy_matches=$(echo "${COMMIT_MSG}" | grep -oE "${HIGH_ENTROPY_PATTERN}" || true)
115+
if [[ -n "${high_entropy_matches}" ]]; then
116+
while IFS= read -r match; do
117+
[[ -z "${match}" ]] && continue
118+
local entropy
119+
entropy=$(calculate_entropy "${match}")
120+
if [[ ${entropy} -gt 10 ]]; then
121+
issues+=("High-entropy string detected (potential secret): ${match:0:20}...")
122+
found_issues=$((found_issues + 1))
123+
break # Only report first high-entropy match
124+
fi
125+
done <<< "${high_entropy_matches}"
126+
fi
127+
128+
# Check for email addresses (warn but don't fail - may be legitimate)
129+
if echo "${COMMIT_MSG}" | grep -qE "${EMAIL_PATTERN}"; then
130+
local emails
131+
emails=$(echo "${COMMIT_MSG}" | grep -oE "${EMAIL_PATTERN}" | sort -u | head -3)
132+
echo -e "${YELLOW}⚠ Warning: Email addresses found in commit message:${NC}"
133+
echo -e " ${emails}"
134+
echo -e "${YELLOW} Ensure these are not sensitive accounts${NC}"
135+
echo ""
136+
fi
137+
138+
# Report issues
139+
if [[ ${found_issues} -gt 0 ]]; then
140+
echo -e "${RED}❌ Commit message contains sensitive information!${NC}"
141+
echo ""
142+
echo -e "${RED}Issues found:${NC}"
143+
for issue in "${issues[@]}"; do
144+
echo -e " ${RED}${NC} ${issue}"
145+
done
146+
echo ""
147+
echo -e "${YELLOW}Commit messages are permanent in git history.${NC}"
148+
echo -e "${YELLOW}Never include:${NC}"
149+
echo -e " - Passwords or credentials"
150+
echo -e " - IP addresses (especially private/internal IPs)"
151+
echo -e " - API keys or tokens"
152+
echo -e " - Any sensitive information"
153+
echo ""
154+
echo -e "${YELLOW}Use generic descriptions instead:${NC}"
155+
echo -e " - 'Remove credentials' instead of 'Remove password xyz123'"
156+
echo -e " - 'Update config' instead of 'Update 192.168.1.100 config'"
157+
echo -e " - 'Fix authentication' instead of 'Fix login with user:pass'"
158+
echo ""
159+
return 1
160+
fi
161+
162+
echo -e "${GREEN}✓ Commit message is safe${NC}"
163+
return 0
164+
}
165+
166+
# Run check
167+
main() {
168+
check_commit_message
169+
}
170+
171+
main "$@"

0 commit comments

Comments
 (0)