跳转至

守卫核心语法自检(check-guard-core.py)

位置:deploy/nfos/tools/check-guard-core.py | 语言:python3 | 是否需要 root:否

工具性质:离线静态自检(只读 security-guard.sh;会写 2 个中间产物) 运行前提:仓库检出必须在场 —— 读 deploy/nfos/security-guard.sh、写 <repo>/.scratch/。可从任意位置运行(工具自动向上找到含 deploy/nfos 的祖先目录) 部署状态:随仓库检出运行,不部署到 /opt/Cy/services/system.d/nfos/


用途

把 deploy/nfos/security-guard.sh 里 heredoc(标记 NFOS_SEC_CORE_EOF)中的 Python 核心整段抽出来,落成 .py 后做 py_compile 语法自检,再对 6 个关键点做字符串复核。

源码 docstring(逐字引用):

抽出 security-guard.sh 里 heredoc 的 Python 核心,做语法自检(py_compile)。

执行流程:

步骤 行为
1 _nf_repo_root() 向上找到含 deploy/nfos 的祖先作为仓库根
2 在 security-guard.sh 中正则查找 <<'NFOS_SEC_CORE_EOF'\n;找不到 → 打印 !! 找不到 heredoc 起始标记 并 SystemExit(1)
3 抽出核心体,写入 <repo>/.scratch/_guard_core.py,打印 heredoc 标记:NFOS_SEC_CORE_EOF;抽出 <N> 行
4 py_compile.compile(..., doraise=True, cfile=<repo>/.scratch/__pycache__/guard_core.pyc);失败 → 打印 py_compile 失败:... 并 SystemExit(1);成功打印 py_compile:通过
5 打印 6 行关键点复核结果(PASS/FAIL 前缀)

6 个关键点复核项(源码里的 checks 列表,逐条):

复核项 判定依据
WA-001/WA-002 调用存在 核心体含 for _sid in ("WA-001", "WA-002"):
读特征库 paths 核心体含 sig.get("paths")
读特征库 threshold 核心体含 th.get("window_lines") 与 th.get("count")
allow.nets 过滤 核心体含 allow_nets
正则编译失败即跳过(不放行) 核心体含 except re.error:
最终输出语句仍在末尾 core.rstrip().endswith('print(json.dumps(OUT, ensure_ascii=False))')

典型场景

  • 改动 security-guard.sh 的 heredoc Python 核心之后的提交前回归(bash -n 看不到 heredoc 内部语法);
  • 守卫出现“什么都没输出/整段静默失败”时,快速区分是语法错误还是逻辑回归(前者被第 4 步拦住,后者被第 5 步的 6 项复核拦住);
  • 与 scan-py-snippets.py(同批工具,检查 heredoc 片段的缺失 import)配套使用:前者查语法与关键语句,后者查 import 完整性。

用法与参数

本工具不接受任何命令行参数(源码不读 sys.argv、无 argparse、无环境变量开关);三条路径全部由仓库根推导。

参数 类型 默认值 说明
(无) — — 输入 <repo>/deploy/nfos/security-guard.sh;产物 <repo>/.scratch/_guard_core.py、<repo>/.scratch/__pycache__/guard_core.pyc

无参数时的默认行为:抽出 heredoc 核心 → 写 .scratch/_guard_core.py → py_compile → 打印 6 项关键点复核。

示例

从仓库根目录执行:

python deploy/nfos/tools/check-guard-core.py

期望输出(实测:退出码 0,抽出行数 655,py_compile:通过,6 项全 PASS):

  heredoc 标记:NFOS_SEC_CORE_EOF;抽出 655 行
  py_compile:通过
  PASS  WA-001/WA-002 调用存在
  PASS  读特征库 paths
  PASS  读特征库 threshold
  PASS  allow.nets 过滤
  PASS  正则编译失败即跳过(不放行)
  PASS  最终输出语句仍在末尾
情形 期望退出码
抽到 heredoc 且 py_compile 通过 0
找不到 heredoc 起始标记 1
py_compile 失败 1
security-guard.sh 缺失 / .scratch/ 不可写 1(Python traceback,非自检语义)

重要退出码陷阱:第 5 步的 6 项关键点复核 FAIL 不会改变退出码 —— 源码只 print,不 sys.exit。也就是说“语法能编译、但关键语句丢了(例如 except re.error: 被删)”会打印 FAIL 却仍返回 0。必须看输出文本,不能只看退出码。

依赖与权限

  • 语言/版本:Python 3。
  • 第三方依赖:无,标准库 io / os / re / py_compile。
  • 仓库依赖:必需 —— 读 deploy/nfos/security-guard.sh;产物写到 <repo>/.scratch/(该目录需已存在且可写,__pycache__ 也需可写,否则 py_compile 报错)。
  • 权限:不需要 root。

风险与注意事项

  • 会写文件(本批工具里少数有持久副作用的之一):
  • <repo>/.scratch/_guard_core.py —— 抽出的 Python 核心全文(约 27 KB),不会被自己清理;
  • <repo>/.scratch/__pycache__/guard_core.pyc —— py_compile 指定的 cfile。
  • 只读线上脚本:security-guard.sh 只被读取,不修改;不部署、不重启守卫、不执行抽取出来的代码(只做编译,不 exec)。
  • 抽取是文本级的:依赖 <<'NFOS_SEC_CORE_EOF' 与结尾同名标记这一对固定写法。若把 heredoc 标记改名或改成非引号形式,第 2 步会直接退出 1。
  • 6 项复核是字符串包含判定,不保证语义等价:核心逻辑被等价重写(例如 except re.error: 改成别的异常类型)会被判 FAIL 而实际没问题;反之,把关键语句挪进永不执行的分支也会判 PASS。
  • 与同批工具共同注意点:这类工具原本写在 .scratch/(仓库根下一级),用 dirname(dirname(__file__)) 当仓库根;提升到 deploy/nfos/tools/(下三级)后该写法失效,已改为 _nf_repo_root() 向上走到第一个含 deploy/nfos 的祖先。因此可以从任何位置运行,但仓库检出必须在场(deploy/nfos/security-guard.sh 与 .scratch/ 都要在同一个检出里)。

回滚

本工具不部署到线上、不改线上系统;不改任何被检查的脚本。

  • 产出/改动位置(均在本地检出内):
  • <repo>/.scratch/_guard_core.py
  • <repo>/.scratch/__pycache__/guard_core.pyc
  • 清理方式:
Remove-Item -Force .scratch/_guard_core.py
Remove-Item -Recurse -Force .scratch/__pycache__

(若 .scratch/__pycache__/ 内还有其它工具的编译产物,请只删 guard_core.pyc。) - 线上侧无需回滚:没有配置、没有服务、没有状态被改动。