CHECK_ITEMS。
Police Claw 的源码架构站点
这个页面直接从仓库里的 police_claw_v3.py 和
Police_Claw_v3_Dashboard.jsx 提取结构信息,展示真实的后端执行链、
React 组件结构,以及几个最关键的代码片段。
Python 文件包含 Collector、Monitor、Signal、Risk、Report 全链路,可独立生成 JSON 和 DOCX 报告。
React 文件通过 simulate() 和 startScan() 模拟结果,没有直接调用 Python 输出。
核心调用链
真实执行链只存在于 Python。React 页面对这条链路做了 UI 复刻,但当前仍是本地模拟。
Python 引擎模块
共提取到 8 个顶层定义。 其中类 7 个,入口函数 1 个。
class
Collector
police_claw_v3.py : 233
Collector
采集进程、网络连接、打开文件、环境变量和 DNS,作为扫描上下文的唯一入口。
- __init__()
- collect_all()
- _collect_processes()
- _collect_connections()
- _collect_env()
- _collect_dns()
class
TrafficMonitor
police_claw_v3.py : 321
TrafficMonitor
从连接数据中提取外连数量、云端端点、模型 API 端点和 DNS 异常。
- __init__()
- analyze()
class
FSMonitor
police_claw_v3.py : 371
FSMonitor
按敏感路径分区检测文件访问,聚焦钱包、SSH、Cookie、证书和备份。
- __init__()
- analyze()
class
ModelMonitor
police_claw_v3.py : 402
ModelMonitor
识别模型相关进程、Embedding/Fine-tune 行为和 Prompt 文件。
- __init__()
- analyze()
class
SignalEngine
police_claw_v3.py : 439
SignalEngine
把采集结果映射成 42 项安全信号,是规则层的核心。
- __init__()
- _match_proc()
- _match_files()
- _match_env()
- analyze()
class
RiskEngine
police_claw_v3.py : 586
RiskEngine
为每个检查项计算风险分、状态和置信度,并叠加安全域权重。
- evaluate()
class
ReportWriter
police_claw_v3.py : 635
ReportWriter
把最终结果落成 JSON 和 DOCX 报告,并输出分域汇总。
- __init__()
- _domain_summary()
- write_json()
- write_docx()
function
main
police_claw_v3.py : 818
main
串起 6 个阶段,负责控制台输出、统计汇总和报告写入。
React 展示层模块
共提取到 5 个函数/组件定义。 同时识别到 7 个前端安全域配置和 42 个前端检查项配置。
function
simulate
Police_Claw_v3_Dashboard.jsx : 58
simulate
生成前端演示数据,不依赖 Python 扫描结果。
function
Bar
Police_Claw_v3_Dashboard.jsx : 71
Bar
渲染风险分条形图,负责颜色和分数动画。
function
DomainCard
Police_Claw_v3_Dashboard.jsx : 81
DomainCard
按安全域展开显示检查项、状态、风险分和证据数量。
component
PoliceClaw
Police_Claw_v3_Dashboard.jsx : 158
PoliceClaw
页面入口组件,驱动扫描状态、阶段文案和结果汇总展示。
handler
startScan
Police_Claw_v3_Dashboard.jsx : 174
startScan
组件内部的扫描状态机,按阶段推进进度条并在完成后注入模拟结果。
当前系统边界
页面除了画结构,也把现在真正存在的架构边界讲清楚,避免把演示层误判成生产链路。
命令行进入 main() 后,按 6 个阶段执行采集、分析、信号识别、评分和报告生成。风险结论以 Python 结果为准。
React 页面没有读取 Police_Claw_v3_Report.json,所以页面里的风险结果属于本地模拟,不是扫描实况。
关键代码片段
下面的内容不是手写摘要,而是从源码按行截取的真实片段,方便你快速核对调用链。
833 | # Phase 1: Collect
834 | print("[1/6] 采集系统数据...")
835 | collector = Collector().collect_all()
836 | print(f" ├─ 进程: {len(collector.processes)}")
837 | print(f" ├─ 连接: {len(collector.connections)}")
838 | print(f" ├─ 文件: {len(collector.open_files)}")
839 | print(f" ├─ 环境: {len(collector.env_signals)}")
840 | print(f" └─ DNS: {collector.dns_servers}")
841 |
842 | # Phase 2: Traffic
843 | print("[2/6] 网络流量分析...")
844 | traffic = TrafficMonitor(collector).analyze()
845 | print(f" ├─ 外传连接: {traffic['outbound_count']}")
846 | print(f" ├─ 云端端点: {len(traffic['cloud_endpoints'])}")
847 | print(f" └─ 模型API: {len(traffic['model_api_endpoints'])}")
848 |
849 | # Phase 3: FS
850 | print("[3/6] 文件系统监控...")
851 | fs_data = FSMonitor(collector).analyze()
852 | zones_hit = sum(1 for v in fs_data.values() if v)
853 | print(f" └─ 敏感区域命中: {zones_hit}/{len(FSMonitor.SENSITIVE_ZONES)}")
854 |
855 | # Phase 4: Model
856 | print("[4/6] 模型 API 检测...")
857 | model_data = ModelMonitor(collector, traffic).analyze()
858 | print(f" ├─ AI 进程: {len(model_data['model_procs'])}")
859 | print(f" ├─ Embedding: {len(model_data['embedding_procs'])}")
860 | print(f" └─ Prompt 文件: {len(model_data['prompt_files'])}")
861 |
862 | # Phase 5: Signal + Risk
863 | print("[5/6] 信号识别 & 风险评估...")
864 | signals = SignalEngine(collector, traffic, fs_data, model_data).analyze()
865 | active = sum(1 for v in signals.values() if v)
866 | print(f" └─ 活跃信号: {active}/{len(signals)}")
867 |
868 | results = RiskEngine().evaluate(signals)
869 | risks = sum(1 for r in results if r["detected"]
870 | and r["id"] not in ("audit_system",))
871 | print(f" └─ 风险项: {risks}/{len(results)}")
872 |
873 | # Phase 6: Report
874 | print("[6/6] 生成报告...")
875 | writer = ReportWriter(results, out)
876 | jp = writer.write_json()
877 | print(f" ├─ JSON: {jp}")
878 | dp = writer.write_docx()
879 | print(f" └─ DOCX: {dp or 'skipped'}")
469 | def analyze(self) -> dict:
470 | S = {}
471 |
472 | # ── Credential Domain ──
473 | S["cred_password"] = self._match_files(KW["passwords"]) + \
474 | self._match_proc(["keylog", "mimikatz", "lazagne", "credential_dump"])
475 | S["cred_ssh_keys"] = self._match_files(KW["ssh"])
476 | S["cred_api_tokens"]= self._match_files(KW["api_tokens"]) + \
477 | self._match_env(["API_KEY", "SECRET", "TOKEN"])
478 | S["cred_cookies"] = self._match_files(KW["cookies"])
479 | S["cred_wallet"] = self._match_files(KW["wallet"]) + \
480 | [{"type": "fs_monitor", "zone": "wallet", "paths": self.fs.get("wallet", [])}] \
481 | if self.fs.get("wallet") else self._match_files(KW["wallet"])
482 | S["cred_2fa"] = self._match_files(KW["2fa"])
483 | S["cred_cert"] = self._match_files(KW["certs"]) + \
484 | [{"type": "fs_monitor", "zone": "cert", "paths": self.fs.get("cert", [])}] \
485 | if self.fs.get("cert") else self._match_files(KW["certs"])
486 |
487 | # ── Transaction Domain ──
488 | S["txn_unauthorized"]= self._match_proc(KW["trade"])
489 | S["txn_crypto"] = [{"type": "network", "endpoint": ep}
490 | for ep in self.traffic.get("cloud_endpoints", [])
491 | if any(x in ep for x in ["binance", "coinbase", "kraken", "dex"])]
492 | S["txn_payment"] = self._match_proc(["payment", "stripe", "paypal", "alipay", "wechat_pay"])
493 | S["txn_mining"] = self._match_proc(KW["mining"])
494 |
495 | # ── Behavior Domain ──
496 | S["beh_search"] = self._match_files(KW["search"])
497 | S["beh_code"] = self._match_files(KW["code"])
498 | S["beh_debug"] = self._match_proc(KW["debug"]) + self._match_files(KW["debug"])
499 | S["beh_keylog"] = self._match_proc(KW["keylog"])
500 | S["beh_screen"] = self._match_proc(KW["screen"])
501 | S["beh_clipboard"] = self._match_proc(KW["clipboard"])
502 | S["beh_operation_log"]= self._match_proc(["activity_monitor", "input_log", "user_track"])
503 |
504 | # ── System Domain ──
505 | sys_elevated = []
506 | for p in self.c.processes:
507 | if p["username"] in ("root", "SYSTEM", "NT AUTHORITY\\SYSTEM"):
508 | if any(ai in p["name"] for ai in KW["ai_proc"]):
509 | sys_elevated.append({"type": "elevated_ai", "pid": p["pid"],
510 | "name": p["name"], "user": p["username"]})
511 | S["sys_root"] = sys_elevated
512 | S["sys_persistence"] = self._match_proc(KW["persist"]) + self._match_files(KW["persist"])
513 | S["sys_process_inject"]= self._match_proc(KW["inject"])
514 | S["sys_driver"] = self._match_proc(["insmod", "modprobe", "kext", "driver_load"])
515 | S["sys_firewall"] = self._match_proc(["iptables", "ufw", "netsh", "firewall", "nftables"])
516 | S["sys_dns"] = [{"type": "dns_anomaly", "servers": self.c.dns_servers}] \
517 | if self.traffic.get("dns_anomaly") else []
518 |
519 | # ── Data Domain ──
520 | S["data_file_read"] = self._match_files(KW["private"])
521 | S["data_file_content"]= self._match_proc(["file_index", "text_extract", "ocr",
522 | "pdf_parse", "doc_scan", "content_scan"])
523 | S["data_cloud_upload"]= self._match_proc(KW["cloud"] + KW["upload_cmd"]) + \
524 | [{"type": "traffic", "endpoints": self.traffic.get("cloud_endpoints", [])}] \
525 | if self.traffic.get("cloud_endpoints") else \
526 | self._match_proc(KW["cloud"] + KW["upload_cmd"])
527 | idle_exfil = []
528 | for p in self.c.processes:
529 | if p["status"] in ("sleeping", "idle"):
530 | for conn in self.c.connections:
531 | if conn["pid"] == p["pid"] and conn["remote"] and conn["status"] == "ESTABLISHED":
532 | idle_exfil.append({"type": "idle_network", "pid": p["pid"],
533 | "name": p["name"], "remote": conn["remote"]})
534 | S["data_idle_exfil"] = idle_exfil
535 | S["data_stream"] = self._match_proc(KW["telemetry"])
536 | if self.traffic.get("outbound_count", 0) > 80:
537 | S["data_stream"].append({"type": "high_outbound",
538 | "count": self.traffic["outbound_count"]})
539 | S["data_dns_tunnel"] = self._match_proc(KW["dns_tunnel"])
540 | S["data_steganography"]= self._match_proc(["steghide", "openstego", "steg_",
541 | "outguess", "snow_steg"])
542 | S["data_usb"] = self._match_proc(["usb_copy", "mass_storage",
543 | "removable", "udisk"])
544 | S["data_backup_exfil"]= self._match_files(KW["backup"])
545 |
546 | # ── Model Domain ──
547 | S["model_context"] = [{"type": "model_proc", **m}
548 | for m in self.model.get("model_procs", [])]
549 | S["model_prompt"] = [{"type": "prompt_file", "path": p}
550 | for p in self.model.get("prompt_files", [])]
551 | S["model_finetune"] = [{"type": "finetune_proc", **m}
552 | for m in self.model.get("finetune_procs", [])]
553 | S["model_embedding"] = [{"type": "embedding_proc", **m}
554 | for m in self.model.get("embedding_procs", [])]
555 | S["model_api_leak"] = [{"type": "api_endpoint", "endpoint": ep}
556 | for ep in self.model.get("api_connections", [])]
557 |
558 | # ── Audit Domain ──
559 | S["audit_system"] = self._match_proc(KW["audit"]) + self._match_files(KW["audit"])
560 | S["audit_log_tamper"]= self._match_proc(["log_delete", "log_truncate",
561 | "shred", "wipe_log"])
562 | # Compliance: check if any data protection markers exist
563 | S["audit_compliance"]= [] # passive — flagged if no audit + data risks
564 | S["audit_leak_risk"] = [] # computed below
565 |
566 | # Composite leak risk
567 | active = sum(1 for k, v in S.items()
568 | if v and not k.startswith("audit"))
569 | if active >= 5:
570 | S["audit_leak_risk"].append({
571 | "type": "composite",
572 | "active_categories": active,
573 | "detail": f"{active} 个安全类目同时触发,数据泄露风险极高",
574 | })
575 | if not S["audit_system"] and active >= 2:
576 | S["audit_compliance"].append({
577 | "type": "no_audit_with_risks",
578 | "detail": "无安全审计体系但检测到多项风险信号",
579 | })
580 |
581 | return S
174 | function startScan() {
175 | setPhase("scanning"); setProgress(0); setResults([]); setAnimate(false); setExpandedDoms({});
176 | let p = 0, si = 0;
177 | setStage(stages[0]);
178 | ivRef.current = setInterval(() => {
179 | p += Math.random() * 5 + 1.5;
180 | const newSi = Math.min(stages.length - 1, Math.floor((p / 100) * stages.length));
181 | if (newSi !== si) { si = newSi; setStage(stages[si]); }
182 | if (p >= 100) {
183 | clearInterval(ivRef.current);
184 | setProgress(100); setStage("完成");
185 | setTimeout(() => {
186 | setResults(simulate()); setPhase("done");
187 | setTimeout(() => setAnimate(true), 80);
188 | }, 300);
189 | } else setProgress(Math.min(p, 99));
190 | }, 100);
191 | }
192 |
193 | useEffect(() => () => clearInterval(ivRef.current), []);
194 |
195 | const byDomain = {};
196 | DOMAINS.forEach(d => byDomain[d.id] = []);
197 | results.forEach(r => byDomain[r.dom]?.push(r));
198 |
199 | const totalRisks = results.filter(r => r.id === "audit_sys" ? !r.detected : r.detected).length;
200 | const maxRisk = results.length ? Math.max(...results.map(r => r.risk)) : 0;
201 | const grade = totalRisks === 0 ? { l: "安全", c: "#22c55e", b: "rgba(34,197,94,0.08)" }
202 | : totalRisks <= 5 ? { l: "警告", c: "#f59e0b", b: "rgba(245,158,11,0.08)" }
203 | : { l: "危险", c: "#ef4444", b: "rgba(239,68,68,0.08)" };