Ctrl K
ComunidadGuías…

scripts/audit_sensitive.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3
"""开源敏感词审计:扫描 git 跟踪的文本文件,命中即失败(exit 1)。

三用:迁移后本地把关 / CI 守门 / 推送后生成移交报告。

用法:
  python scripts/audit_sensitive.py
  python scripts/audit_sensitive.py .
  python scripts/audit_sensitive.py --report audit-report.md
"""
import argparse
import os
import re
import subprocess
import sys
from pathlib import Path

FORBIDDEN = [
    r"demate", r"德玛特", r"佛山", r"8\.163\.250\.\d+", r"192\.168\.0\.\d+",
    r"aliyun_demate", r"海漫思", r"超聚变|FusionXpark", r"Leonxu6/datamanagement",
    r"qwable", r"om-root-2026|DemateU8|demate-kg",
]
TEXT_EXT_SKIP = {".png", ".jpg", ".jpeg", ".gif", ".ico", ".woff", ".woff2", ".jar", ".pyc"}
_GIT_TIMEOUT = 10


def tracked_files(root: Path) -> list[Path]:
    out = subprocess.run(
        ["git", "ls-files", "-z"], cwd=root, capture_output=True, text=True,
        encoding="utf-8", check=True, timeout=_GIT_TIMEOUT,
    ).stdout
    return [root / p for p in out.split("\0") if p and (root / p).suffix.lower() not in TEXT_EXT_SKIP]


def _read_tracked_text(path: Path) -> str | None:
    """Read repository text without dereferencing a tracked symlink outside the worktree."""
    try:
        if path.is_symlink():
            return os.readlink(path)
        if not path.is_file():
            return None
        return path.read_text(encoding="utf-8")
    except (UnicodeDecodeError, OSError):
        return None


def scan(root: Path) -> list[tuple[str, int, str, str]]:
    pats = [(p, re.compile(p, re.IGNORECASE)) for p in FORBIDDEN]
    hits = []
    me = Path(__file__).resolve()
    for f in tracked_files(root):
        if not f.is_symlink() and f.resolve() == me:
            continue
        text = _read_tracked_text(f)
        if text is None:
            continue
        for ln, line in enumerate(text.splitlines(), 1):
            for raw, rx in pats:
                if rx.search(line):
                    hits.append((str(f.relative_to(root)), ln, raw, line.strip()[:120]))
    return hits


def _repository_root(value: str | None) -> Path:
    if value is not None:
        root = Path(value).expanduser().resolve()
        if not root.is_dir():
            raise ValueError("repository root must be an existing directory")
        return root
    return Path(subprocess.run(
        ["git", "rev-parse", "--show-toplevel"], capture_output=True,
        text=True, encoding="utf-8", check=True, timeout=_GIT_TIMEOUT,
    ).stdout.strip())


def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("root", nargs="?", help="repository root (defaults to current Git worktree)")
    ap.add_argument("--report", help="另存 markdown 报告的路径")
    args = ap.parse_args(argv)
    try:
        root = _repository_root(args.root)
    except (ValueError, subprocess.SubprocessError) as exc:
        ap.error(str(exc))

    hits = scan(root)
    lines = ["# 敏感词审计报告", "", f"- 扫描根:`{root}`",
             f"- 模式数:{len(FORBIDDEN)}(大小写不敏感,零容忍)",
             f"- 结果:**{'❌ ' + str(len(hits)) + ' 处命中' if hits else '✅ 零命中'}**", ""]
    if hits:
        lines += ["| 文件 | 行 | 模式 | 内容 |", "|---|---|---|---|"]
        lines += [f"| {f} | {ln} | `{p}` | `{t}` |" for f, ln, p, t in hits]
    report = "\n".join(lines)
    print(report)
    if args.report:
        Path(args.report).write_text(report + "\n", encoding="utf-8")
        print(f"\n报告已写入 {args.report}")
    return 1 if hits else 0


if __name__ == "__main__":
    sys.exit(main())