Files
zhuangang/tools/check_sensitive.py
T

68 lines
2.8 KiB
Python

# tools/check_sensitive.py —— 提交前扫描暂存区内容
# 不自动运行:作为 pre-commit 钩子在仓库根目录执行;加 --self-test 可以单独自检
import re
import subprocess
import sys
from pathlib import Path
PATTERNS = {
"内网 IP": re.compile(r"\b10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b|\b192\.168\.\d{1,3}\.\d{1,3}\b|\b172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}\b"),
"疑似密钥": re.compile(r"(?i)\b(?:sk|ak|api[_-]?key|secret|token)[\w-]*\s*[:=]\s*['\"]?[A-Za-z0-9_\-]{16,}"),
"私钥": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
"root 绝对路径": re.compile(r"(?<![\w/])/root/\S+"), # sensitive-ok
}
WORDS_FILE = Path("notes/private/sensitive-words.txt")
ALLOW_MARK = "sensitive-ok" # 行内写这个标记表示人工确认过,可以放行(比如文档里讲解正则的例子)
def load_words() -> list[str]:
if not WORDS_FILE.exists():
return []
return [w.strip() for w in WORDS_FILE.read_text(encoding="utf-8").splitlines() if w.strip() and not w.startswith("#")]
def scan_text(name: str, text: str, words: list[str]) -> list[str]:
problems = []
for no, line in enumerate(text.splitlines(), start=1):
if ALLOW_MARK in line:
continue
for label, pat in PATTERNS.items():
if pat.search(line):
problems.append(f"{name}:{no} {label}")
for w in words:
if w.lower() in line.lower():
problems.append(f"{name}:{no} 敏感词(见私有清单)") # 不打印敏感词本身
return problems
def staged_files() -> list[str]:
out = subprocess.run(["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"],
capture_output=True, text=True, check=True).stdout
return [f for f in out.splitlines() if f]
def main() -> int:
words = load_words()
problems = []
for f in staged_files():
content = subprocess.run(["git", "show", f":{f}"], capture_output=True).stdout # 读暂存区版本
try:
problems += scan_text(f, content.decode("utf-8"), words)
except UnicodeDecodeError:
continue # 跳过二进制文件
for p in problems:
print("✗", p)
if problems:
print("发现疑似敏感内容,已阻止提交。确认无误的行可加注释 sensitive-ok。")
return 1 if problems else 0
if __name__ == "__main__" and "--self-test" in sys.argv:
sample = "url: http://192.168.1.20:8080\napi_key = 'abcdefghijklmnopqrstuvwxyz'\n正常的一行\n" # sensitive-ok
found = scan_text("demo.md", sample, ["某客户"])
assert len(found) == 2, found
assert scan_text("demo.md", "某客户的需求", ["某客户"]) == ["demo.md:1 敏感词(见私有清单)"]
print("自检通过:", found)
elif __name__ == "__main__":
sys.exit(main())