守卫核心语法自检(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(逐字引用):
执行流程:
| 步骤 | 行为 |
|---|---|
| 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 项关键点复核。
示例¶
从仓库根目录执行:
期望输出(实测:退出码 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 不会改变退出码 —— 源码只
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- 清理方式:
(若 .scratch/__pycache__/ 内还有其它工具的编译产物,请只删 guard_core.pyc。)
- 线上侧无需回滚:没有配置、没有服务、没有状态被改动。