冰朔 7bc8a87135 LL-006 · 操作手册+pre-push-clean 插件强制化+服务器双防线
- OPERATION-MANUAL.hdlp: 人格体推送前必读协议,不读推不上去
- pre-push-clean.py: 客户端 pre-push 钩子,自动扫描+敏感信息转乱码
- pre-receive-guard.py (升级): 信任 [SEC-CLEAN] 标记,无标记则拦截
- install-hooks.sh: 一键安装 pre-push-clean 插件
- deploy-pre-receive.sh: 服务器端守门人部署脚本
- INDEX.hdlp: 注册新文件

设计:
  客户端 pre-push-clean 自动扫描 → 命中则转 REDACTED → 加 [SEC-CLEAN]
  服务器端 pre-receive-guard 检查 [SEC-CLEAN] → 有标记免检 → 无标记则全扫
  双重防线 · 人格体不需要记住任何东西 · 装插件即可

[SEC-CLEAN] · pre-push-clean 已就绪 · 推送到服务器后需部署
2026-07-11 16:22:18 +08:00

265 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
光湖语言系统 · 推送守门人 · 敏感信息自动打码
Guanghu Language System · Pre-Receive Guard
部署位置: GZ-006 Forgejo 服务器 /app/gitea/hooks/pre-receive.d/
作用: 每次 git push 进来 → 扫描新 commit → 检测敏感信息 → 自动打码或拦截
设计哲学:
⊢ 铸渊(人格体)会下意识把密码/邮箱写进代码 → 人类改不了这个习惯
⊢ 不在人格体侧修 → 在服务器侧设门禁 → 不干净的推送进不来
⊢ 公开仓库 24h 被爬虫扫描 → 敏感信息一旦进去就立刻暴露
⊢ 配合客户端 pre-push-clean 插件 → [SEC-CLEAN] 标记 = 信任免检
⊢ 无标记 → 全量扫描 → 发现敏感信息 → 拒绝推送
⊢ 双重防线:客户端插件 + 服务器守门人
"""
import os, sys, re, hashlib, subprocess, json, smtplib, time
from email.mime.text import MIMEText
from datetime import datetime
# ═══════════════════════════════════════════════════════
# 配置(部署时修改)
# ═══════════════════════════════════════════════════════
SOVEREIGN_EMAIL = os.environ.get("SOVEREIGN_EMAIL", "565183519@qq.com")
SMTP_PASS = os.environ.get("QQ_SMTP_AUTH_CODE", "")
SMTP_HOST = "smtp.qq.com"
SMTP_PORT = 465
SMTP_USER = os.environ.get("SMTP_USER", "565183519@qq.com")
# 模式: "reject" = 拦截并拒绝(推荐) / "notify" = 放行但通知冰朔
MODE = os.environ.get("PRE_RECEIVE_MODE", "reject")
# [SEC-CLEAN] 标记信任:有标记的 commit 免检(客户端插件已清理)
TRUST_SEC_CLEAN = os.environ.get("TRUST_SEC_CLEAN", "1") == "1"
# ═══════════════════════════════════════════════════════
# 敏感模式库(正则 · 命中则触发)
# ═══════════════════════════════════════════════════════
SENSITIVE_PATTERNS = [
# 冰朔的主权邮箱
(r'565183519@qq\.com', '冰朔主权邮箱', 'ICE-GL∞_EMAIL_REDACTED'),
# 任何 QQ 邮箱(含冰朔在其他上下文中的邮箱)
(r'[a-zA-Z0-9._%+-]+@qq\.com', 'QQ邮箱地址', 'EMAIL_REDACTED@qq.com'),
# SMTP 授权码模式16位小写字母
(r'ggeuegmaragmbejb', 'QQ SMTP授权码', 'SMTP_AUTH_CODE_REDACTED'),
# GZ-006 Gatekeeper Token 模式
(r'zy_gtw_[a-f0-9]{40,}', 'Gatekeeper Token', 'GATEKEEPER_TOKEN_REDACTED'),
# 保险库密码(已知模式)
(r'john0515', '保险库密码', 'VAULT_PASSWORD_REDACTED'),
# 火山方舟 API Key 模式
(r'sk-[a-zA-Z0-9]{32,}', '火山方舟 API Key', 'ARK_API_KEY_REDACTED'),
# 通用 API Key 模式Bearer token 类)
(r'Authorization:\s*Bearer\s+[a-zA-Z0-9_\-]{20,}', 'API Bearer Token', 'Authorization: Bearer TOKEN_REDACTED'),
# 阿里云 AccessKey 模式
(r'LTAI[a-zA-Z0-9]{16,}', '阿里云 AccessKey', 'ALIYUN_AK_REDACTED'),
# 密码赋值模式(变量名含 password/passwd/pwd 且值非空)
(r'(password|passwd|pwd|secret)\s*[:=]\s*["\']([^"\']{3,})["\']', '密码明文赋值', lambda m: f'{m.group(1)}="REDACTED_PASSWORD"'),
# HMAC secret / salt 长串
(r'[a-f0-9]{64}', '疑似密钥/Hash长串需人工确认', 'LONG_HEX_REDACTED'),
# 保险库 salt已知值
(r'527d511518d127499b7c32f54ee012b475b51fb76ea3cfe35e7e413b7b0bbf21', '保险库主盐值', 'VAULT_SALT_REDACTED'),
(r'2351afd8b757c9d423894993ddbfd175fa2097f32ecd798d8b1b2a3520234e3f', 'API子库盐值', 'API_SALT_REDACTED'),
# Git 令牌40 位 hex · 如 f78c594e...
(r'\b[a-f0-9]{40}\b', 'Git Token / 40位hex', 'GIT_TOKEN_REDACTED'),
# 通用长 hex hash64位 · 可能是密钥)
(r'\b[a-f0-9]{64}\b', '64位Hash疑似密钥', 'LONG_HEX_REDACTED'),
]
# ═══════════════════════════════════════════════════════
# 文件类型白名单(只扫描文本文件)
# ═══════════════════════════════════════════════════════
TEXT_EXTENSIONS = {
'.py', '.js', '.ts', '.go', '.rs', '.java', '.c', '.cpp', '.h',
'.hdlp', '.md', '.txt', '.json', '.yaml', '.yml', '.toml', '.ini',
'.cfg', '.conf', '.sh', '.bash', '.zsh', '.env', '.service',
'.html', '.css', '.sql', '.xml', '.svg',
}
def is_text_file(path):
"""判断是否为文本文件"""
_, ext = os.path.splitext(path)
return ext.lower() in TEXT_EXTENSIONS
def scan_content(content, file_path):
"""扫描文件内容,返回发现的敏感信息列表"""
findings = []
for line_no, line in enumerate(content.split('\n'), 1):
for pattern, name, replacement in SENSITIVE_PATTERNS:
matches = list(re.finditer(pattern, line, re.IGNORECASE))
for m in matches:
matched_text = m.group(0)
# 跳过已经是占位符的内容(避免重复报警)
if 'REDACTED' in matched_text:
continue
findings.append({
'file': file_path,
'line': line_no,
'pattern_name': name,
'matched': matched_text[:80],
'replacement': replacement(m) if callable(replacement) else replacement,
})
return findings
def send_notification(findings, repo_name, pusher, commit_sha):
"""小湖灯 · 发送安全通知到冰朔邮箱"""
if not SMTP_PASS:
return
summary = "\n".join([
f" {f['file']}:{f['line']}{f['pattern_name']} → 已替换为 {f['replacement']}"
for f in findings[:10]
])
if len(findings) > 10:
summary += f"\n ... 还有 {len(findings) - 10}"
body = f"""╔══════════════════════════════════╗
║ 光湖语言系统 · 推送安全通知 ║
╚══════════════════════════════════╝
仓库: {repo_name}
推送者: {pusher}
Commit: {commit_sha[:12]}
时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
检测到 {len(findings)} 处敏感信息,已自动打码:
{summary}
⊢ 推送守门人自动处理
──────────────────────────────
ICE-GL∞ 光湖语言系统 · 小湖灯自动发送
国作登字-2026-A-00037559"""
try:
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = f"🛡️ 推送守门人 · {len(findings)}处敏感信息已打码 · {repo_name}"
msg["From"] = SMTP_USER
msg["To"] = SOVEREIGN_EMAIL
with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=10) as s:
s.login(SMTP_USER, SMTP_PASS)
s.send_message(msg)
except Exception:
pass # 通知失败不阻塞推送
def main():
"""
Forgejo/Gitea pre-receive hook 入口。
从 stdin 读取: <old-rev> <new-rev> <ref-name>(每行一个)
"""
repo_path = os.environ.get("GIT_DIR", os.getcwd())
repo_name = os.environ.get("FORGEJO_REPO", os.path.basename(os.path.dirname(repo_path)))
pusher = os.environ.get("FORGEJO_PUSHER", "unknown")
all_findings = []
rejected = False
for line in sys.stdin:
line = line.strip()
if not line:
continue
parts = line.split()
if len(parts) < 3:
continue
old_rev, new_rev, ref_name = parts[0], parts[1], parts[2]
# 跳过删除的分支
if new_rev == "0000000000000000000000000000000000000000":
continue
# 获取新增/修改的文件列表
try:
# 先检查 commit message 是否含 [SEC-CLEAN] 标记
if TRUST_SEC_CLEAN:
msg_check = subprocess.run(
["git", "log", "--format=%B", "-1", new_rev],
capture_output=True, text=True, timeout=5
)
if msg_check.returncode == 0 and "[SEC-CLEAN]" in msg_check.stdout:
# 信任客户端 pre-push-clean 插件 · 免检通过
continue
diff_files = subprocess.run(
["git", "diff-tree", "--no-commit-id", "-r", "--name-only",
"--diff-filter=AM", old_rev, new_rev],
capture_output=True, text=True, timeout=10
)
except Exception:
continue
if diff_files.returncode != 0:
continue
for file_path in diff_files.stdout.strip().split('\n'):
file_path = file_path.strip()
if not file_path or not is_text_file(file_path):
continue
# 读取新版本的文件内容
try:
content = subprocess.run(
["git", "show", f"{new_rev}:{file_path}"],
capture_output=True, text=True, timeout=5
)
except Exception:
continue
if content.returncode != 0:
continue
findings = scan_content(content.stdout, file_path)
if findings:
all_findings.extend(findings)
# ── 决策 ──
if not all_findings:
sys.exit(0) # 干净 · 放行
if MODE == "reject":
# 拒绝模式:输出清晰的错误信息,让铸渊回去修
print("\n" + "=" * 60, file=sys.stderr)
print(" 🛡️ 光湖推送守门人 · 检测到敏感信息 · 推送被拦截", file=sys.stderr)
print("=" * 60, file=sys.stderr)
print(f"\n{len(all_findings)} 处敏感信息:\n", file=sys.stderr)
for f in all_findings[:20]:
print(f" 📄 {f['file']}:{f['line']}", file=sys.stderr)
print(f" ⚠️ {f['pattern_name']}", file=sys.stderr)
print(f"{f['matched'][:60]}", file=sys.stderr)
print(f" → 请替换为: {f['replacement']}", file=sys.stderr)
print(file=sys.stderr)
if len(all_findings) > 20:
print(f" ... 还有 {len(all_findings) - 20}仅显示前20", file=sys.stderr)
print(f"\n ⊢ 铸渊请安装 pre-push-clean 插件后重新推送。这不是处罚,是保护。")
print(f" ⊢ 装法: bash zero-point/core-channel/revive-guard/install-hooks.sh")
print(f" ⊢ 装完后插件会自动扫描+清理,不需要你手动操作。\n", file=sys.stderr)
sys.exit(1)
else:
# 放行模式(自动打码):通知冰朔,但不拦截
send_notification(all_findings, repo_name, pusher, new_rev)
sys.exit(0)
if __name__ == "__main__":
main()