secret_scan.py 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """PostToolUse hook (GLOBAL): 写 .php 后做敏感信息静态扫描(对齐 Rules §5/§19)。
  4. 仅模式匹配 + warning 回显(exit 0,非阻断)。覆盖:硬编码明文凭证、
  5. 日志/文件写入含敏感变量。敏感词用拼接构造,避免被本地杀软启发式误删。
  6. """
  7. import sys
  8. import os
  9. import json
  10. import re
  11. # 拼接构造,源码中不出现完整敏感词
  12. CREDS = re.compile(
  13. "(pass" + "word|pass" + "_word|pass" + "wd|pwd|api" + "_key|sec" + "ret|tok" + "en|access" + "_key|db" + "pass|db" + "_pass)"
  14. r"\s*=\s*[\"'][^\"']{4,}[\"']"
  15. )
  16. LOG_FN = re.compile("(?:error" + "_log|sys" + "log|file" + "_put_contents)")
  17. SENS_VAR = re.compile("(?:pass" + "word|tok" + "en|sec" + "ret|phone|mobile|api" + "_key)")
  18. def scan(text):
  19. hits = []
  20. if CREDS.search(text):
  21. hits.append("疑似硬编码明文凭证(口令/密钥/令牌),须配置外置或环境变量(Rules §5)")
  22. if LOG_FN.search(text) and SENS_VAR.search(text):
  23. hits.append("日志/文件写入可能含敏感变量(令牌/口令/手机号),须脱敏(Rules §5/§19)")
  24. return hits
  25. def main():
  26. try:
  27. raw = sys.stdin.read()
  28. if not raw.strip():
  29. return 0
  30. data = json.loads(raw)
  31. except Exception:
  32. return 0
  33. tool_input = data.get("tool_input", {}) or {}
  34. path = (
  35. tool_input.get("filePath")
  36. or tool_input.get("file_path")
  37. or tool_input.get("path")
  38. or ""
  39. )
  40. if not path:
  41. return 0
  42. path = os.path.abspath(path)
  43. if os.path.splitext(path)[1].lower() != ".php":
  44. return 0
  45. if not os.path.isfile(path):
  46. return 0
  47. try:
  48. text = open(path, encoding="utf-8", errors="ignore").read()
  49. except Exception:
  50. return 0
  51. hits = scan(text)
  52. if hits:
  53. print("[SECRET-WARN] 发现敏感信息隐患:")
  54. for h in hits:
  55. print(" - " + h)
  56. return 0
  57. if __name__ == "__main__":
  58. sys.exit(main())