- 将 7 个文档中的 mac 绝对路径链接(file:///Users/qingsong/...)转换为仓库相对路径, 并修正错误目标:aibox 入口 main.py → aibox.py、pymsg C++ 实现 → libmsg/inc/us/Msg.hpp、 bot4cj cpp/cnn → cuda - 主 README 补 liblogc 基础库条目与 confs2 归档说明,安装顺序补 ca - 新增 liblogc / libtry / confs2 / 3d / memdb README - 同步 db2 脚本迁移(db/create_sqlite3.sql → db2/scripts/)导致的失效引用
26 KiB
db2 模块安全审计报告
初次审计: 2026-08-06
最近复审: 2026-08-07(第六轮审计,全部问题已修复,编译验证通过)
审计范围: db2/src/db2.cpp、Makefile、docker-entrypoint.sh
对照基准: Python 版 db/dbv2.py(接口兼容性对比)
代码版本: main 分支 + 第六轮修复
修复进度
| 优先级 | 总数 | 已修复 | 未修复 |
|---|---|---|---|
| P0 | 2 | 2 | 0 |
| P1 | 1 | 1 | 0 |
| P2 | 4 | 4 | 0 |
| P3 | 20 | 20 | 0 |
| 合计 | 26 | 26 | 0 |
P0 — 严重问题
P0-1: 列名 SQL 注入(列名未校验直接拼接 SQL)
-
位置: 历史版本 L399-455 等位置
-
当前状态: ✅ 已修复(2026-08-06)
-
修复: 新增
_validate_col静态方法 db2.cpp L443-449,对所有列名(where keys、columns、order_by、INSERT/UPDATE values keys)校验其存在于表 schema 的col_map中,不存在则抛"unknown column"错误。5 个注入点全部修复。sqlite3_prepare_v2只编译单条语句(; DROP TABLE不会执行),但单语句内注入在修复前仍然可行。修复后列名必须是真实存在的列名,注入不可能。 -
注入点覆盖:
P0-2: 空 in 数组 → 无 WHERE 子句 → 全表删除/更新
- 位置:
_build_wherein运算符分支 - 当前状态: ✅ 已修复(2026-08-06)
- 修复: L503-506 空
in数组现在生成恒假条件parts.push_back("0"),使 WHERE 子句匹配零行,与 Python 版 SQLAlchemycolumn.in_([])行为一致。
P1 — 高危问题
P1-1: 所有值绑定为 TEXT,类型信息丢失
-
位置: 历史版本
_coerce_value/_bind_value -
当前状态: ✅ 已修复(2026-08-06)
-
修复:
_coerce_valueL414-420 现在返回boost::json::value,仅对字符串做$now替换,其他类型保持原样。- 新增
_bind_valueL422-440,按 JSON 类型分别调用sqlite3_bind_int64/sqlite3_bind_double/sqlite3_bind_text/sqlite3_bind_null/sqlite3_bind_int(bool)。 WhereClause::params改为std::vector<json::value>,保留原始类型。
修复后 INTEGER/REAL 等数值类型以原生 SQLite 存储类写入,避免 TEXT 列上的字符串比较问题,行为与 Python 版一致。
P2 — 中危问题
P2-1: 清理时间戳格式不一致(T vs 空格)导致保留期偏移约 1 天
- 位置: 历史版本
utc_now+_do_cleanup - 当前状态: ✅ 已修复(2026-08-07)
- 修复: L863-865 清理 SQL 改用
strftime('%Y-%m-%dT%H:%M:%S','now','-N days'),与utc_now()的T分隔格式完全对齐。
P2-2: 缺少配置完整性校验
- 位置: 历史版本
load_config - 当前状态: ✅ 已修复(2026-08-07)
- 修复: L159-175 在 INI 解析后,对 7 个必需字段做必填校验,缺失任一则
fprintf明确错误信息后exit(1)。
P2-3: cleanup_interval 无下限校验,配置为 0 或负数时导致清理线程 busy loop 占满 CPU
- 位置:
_cleanup_loop、load_config - 当前状态: ✅ 已修复(2026-08-07)
- 修复: db2.cpp L207-L227 在
load_config返回前(所有配置来源——INI、环境变量、命令行参数——都加载完毕后)统一增加数值范围校验,非法值立即fprintf明确错误信息后exit(1)。 - 校验项:
listen_port: 1-65535workers: ≥ 1cleanup_interval: ≥ 1(busy loop 问题根源)retention_days: ≥ 0max_records: ≥ 1
P3 — 低危问题
| # | 问题 | 位置 | 说明 |
|---|---|---|---|
| P3-1 | schema 缓存永不失效 | 历史版本 | ✅ 已修复:缓存项携带 PRAGMA schema_version L270-273,每次取 schema 前比对版本号;DDL 变更后自动失效并重新反射 |
| P3-2 | _coerce_value 对 null 返回 "" |
历史版本 | ✅ 已修复:_coerce_value 改为返回 json::value,null 保持为 null |
| P3-3 | limit/offset 未校验负值 |
历史版本 | ✅ 已修复:L622-632 非负校验 |
| P3-4 | WHERE 条件数 / IN 数组大小无上限 | 历史版本 | ✅ 已修复:L463-467 64 条件上限、L507-511 IN 元素 1000 上限 |
| P3-5 | 空 values 对象导致 INSERT/UPDATE 语法错误 |
历史版本 | ✅ 已修复:L680-682、L724-726 前置校验 |
| P3-6 | 未知 WHERE 运算符被静默跳过 | 历史版本 | ✅ 已修复:L519-521 未知运算符抛错 |
| P3-7 | utc_now() 使用非线程安全的 gmtime() |
L58-66 | ✅ 已修复(2026-08-07,第五轮):改用 gmtime_r(&t, &tmbuf) 线程安全版本,消除多线程并发调用时的静态缓冲区数据竞争 |
| P3-8 | 其他数值配置无范围校验 | L186-L191 | ✅ 已修复(2026-08-07,与 P2-3 一同):L207-L227 所有数值配置项范围校验覆盖:port 1-65535、workers ≥1、cleanup_interval ≥1、retention_days ≥0、max_records ≥1 |
| P3-9 | columns 数组中非字符串元素未检查类型 |
L633-636 | ✅ 已修复(2026-08-07,第五轮):添加 if (!c.is_string()) throw "columns array must contain only strings" 类型校验,非字符串元素抛清晰错误而非 boost 原始异常 |
| P3-10 | DbConn 析构使用 sqlite3_close 而非 sqlite3_close_v2 |
L254-256 | ✅ 已修复(2026-08-07,第五轮):改用 sqlite3_close_v2,异常路径存在未 finalize 的 stmt 时标记为 zombie 状态,等所有 stmt 释放后自动关闭,更鲁棒 |
| P3-11 | std::stoi/stoll 解析配置失败无友好错误提示 |
L72-87、L163、L208-214、L218-219、L226-227 | ✅ 已修复(2026-08-07,第五轮):引入 safe_stoi(s, field_name) 辅助函数,所有 std::stoi 调用点(CONFS_PORT、INI 配置、环境变量、命令行参数共 9 处)替换为 safe_stoi,失败时抛带字段名的友好错误(如 "invalid [server] port: 'abc' (stoi)") |
| P3-12(新) | _log_internal 静默吞掉所有异常 |
L898-900 | ✅ 已修复(2026-08-07,第五轮):catch (...) 改为 catch (const std::exception& e),并 fprintf(stderr, "[db2] _log_internal failed: %s", e.what()),便于排查日志写入失败原因,仍不阻塞业务 |
| P3-13(新) | _do_cleanup COUNT 查询 prepare 失败静默跳过 |
L947-951 | ✅ 已修复(2026-08-07,第五轮):prepare 失败时 fprintf(stderr, ...) 记录 sqlite3_errmsg,同时简化 finalize 逻辑为单一出口 if (stmt) sqlite3_finalize(stmt),避免重复 finalize 风险 |
| P3-14(新) | order_by 非法元素静默跳过 |
L654-671 | ✅ 已修复(2026-08-07,第五轮):非 object 元素、缺少 column 字段、column 非 string、desc 非 bool 均改为抛 runtime_error 明确报错,不再静默跳过,避免掩盖客户端错误 |
| P3-15(新) | action 字段非 string 静默默认为 "select" |
L596-600 | ✅ 已修复(2026-08-07,第五轮):action 字段存在但非 string 类型时抛 "'action' must be a string",不再静默回退为 "select" 导致意外执行查询 |
| P3-16(新) | order_by 的 column/desc 字段类型未校验 |
L658-668 | ✅ 已修复(2026-08-07,第五轮,与 P3-14 一同):column 字段检查 is_string(),desc 字段检查 is_bool(),类型不符时抛清晰错误而非 boost 原始异常 |
第六轮审计新增问题(2026-08-07)
ND-1(P2): start() 异常路径导致 std::terminate() 硬崩溃
- 位置:
start()L341-355 - 当前状态: ✅ 已修复(2026-08-07,第六轮)
- 问题: 清理线程 L343 启动后,若
rpc.start()抛出异常(socket/bind/listen 失败如端口被占用,或事件循环异常经MsgServer::start()重新抛出),原有的_cleanup_running=false; notify; join正常清理路径被跳过。栈展开时DB2Server析构,std::thread成员仍 joinable →std::terminate()进程硬崩溃,并掩盖真实错误(本应干净退出返回错误码)。 - 修复: 引入 RAII 作用域守卫
CleanupGuardL348-355,析构时停止并 join 清理线程。无论rpc.start()正常返回还是抛出,清理线程都被优雅回收,异常沿调用链传播到main的 catch 返回错误码。
ND-4(P3): limit/offset/where 类型不符被静默忽略
- 位置:
_do_selectlimit/offset L692-708、_handle_executewhere L626-630 - 当前状态: ✅ 已修复(2026-08-07,第六轮)
- 问题:
limit/offset仅当is_int64()才应用,where仅当is_object()才解析;调用方传错类型(字符串/浮点/数组)时字段被静默丢弃,出现"看似生效实则未生效"的静默偏差,掩盖客户端错误。 - 修复:
limit/offset存在但非整型、where存在但非对象时抛清晰错误(与 P3-15 action 校验一致)。
ND-5(P3): _bind_value uint64 超 INT64_MAX 截断溢出
- 位置:
_bind_valueL480-485 - 当前状态: ✅ 已修复(2026-08-07,第六轮)
- 问题:
jv.as_uint64()经static_cast<sqlite3_int64>直接截断,超出 INT64_MAX 的 uint64 值发生回绕,数值静默失真。 - 修复: L480-483 绑定前校验
u > INT64_MAX则抛错,防止静默数值截断。
ND-6(P3): in 运算符非数组值被静默当作空数组
- 位置:
_build_wherein 分支 L558-562 - 当前状态: ✅ 已修复(2026-08-07,第六轮)
- 问题:
in值为字符串/数字等非数组时被当作空数组 → 恒假条件,静默掩盖客户端错误。 - 修复: L558-559 非数组立即抛
"'in' operator requires an array value";仅空数组保留恒假0语义(与 Python 版column.in_([])一致)。
已知限制(设计决策,非缺陷)
- ND-2(L-13):
select无结果集上限 — 无 WHERE + 无limit时SELECT *返回全表并整体装入内存,恶意/异常客户端可触发内存耗尽 DoS。为使调用方零改动切换而不加隐式 limit,记录为已知限制;建议 gateway 侧对响应做大小/行数限制,或后续引入可配置max_result_rows(默认关闭以保兼容)。 - ND-3(L-14):
update/delete无 WHERE 时全表更新/删除,无内置防护 — 与 Python 版行为对齐,属兼容性权衡;调用方漏传 where 会静默作用于全表,属数据安全风险。建议调用方侧强制校验,或后续提供可选require_where开关。
已有安全机制(正确实现)
- 表名校验 — L558-562:仅允许
[A-Za-z0-9_],防止表名级 SQL 注入 - 列名白名单校验 —
_validate_colL443-449:5 个拼接点全部覆盖,列名必须存在于表 schema - 值参数化绑定 —
_bind_valueL422-440:按 JSON 类型分别sqlite3_bind_*,无值级 SQL 注入 - per-thread 连接 — L337-341:
thread_local独占连接,避免跨线程共享 prepared statement - 异常脱敏 —
RpcServer::_sanitize_error(libmsg):正则替换绝对路径为<redacted> - 仅操作已有表 —
PRAGMA table_info反射,无法建表/删表/执行 DDL - 空
in恒假条件 — L503-506:空数组不忽略 WHERE,匹配零行 - SQLite PRAGMA 优化 — L223-228:WAL + synchronous=NORMAL + foreign_keys=ON + busy_timeout=5000 + mmap_size=256MB
- schema 缓存加锁 + 版本感知失效 —
_schema_mtxL326 保护缓存;PRAGMA schema_version自动感知 DDL 变更 - 配置完整性校验 — L159-175:7 个必需字段缺失则
exit(1) - confs 引导重试 —
retry_on_refused=true,不要求服务启动顺序 - 日志内部直写 —
_log_internalL814-843 绕过 RPC 防递归,异常静默不阻塞业务 - 后台清理线程 — 条件变量优雅停止 L847-856,时间+条数双策略清理,时间戳格式 T 对齐
- 空 values 前置校验 — INSERT/UPDATE 空 values 立即报错
- WHERE 运算符白名单 — 未知运算符立即抛错,不静默跳过
limit/offset非负校验 — 负值直接报错- WHERE / IN 输入规模上限 — 64 条件 / 1000 IN 元素,防资源耗尽
- 日志截断 —
kMaxMessage=8192、kMaxExtra=4096,防止超长日志耗尽内存 - 不可拷贝连接 —
DbConn显式= delete拷贝构造/赋值,防止 double free - 数值配置范围校验 — L207-L227:listen_port 1-65535、workers ≥1、cleanup_interval ≥1、retention_days ≥0、max_records ≥1,非法值立即
exit(1),防止误配置导致 busy loop/绑定失败等异常行为 - 线程安全时间函数 —
utc_now()使用gmtime_r而非gmtime,消除多线程并发写日志时的静态缓冲区数据竞争(P3-7 修复) - 安全 sqlite3 关闭 —
DbConn析构使用sqlite3_close_v2,异常路径未 finalize 的 stmt 不会导致连接泄漏(P3-10 修复) - 友好配置解析错误 —
safe_stoi(s, field_name)包装所有std::stoi调用点,失败时抛带字段名的错误信息(P3-11 修复) - 全量 JSON 字段类型校验 —
action/columns/order_by的column+desc字段均检查 JSON 类型,类型不符抛清晰错误而非 boost 原始异常(P3-9/P3-15/P3-16 修复) - 日志写入失败可观测 —
_log_internalcatch 块记录 stderr,不再静默吞异常(P3-12 修复) - 清理线程错误可观测 —
_do_cleanupCOUNT 查询 prepare 失败时记录 stderr(P3-13 修复) - 清理线程 RAII 守卫 —
start()使用CleanupGuard,rpc.start()异常路径下清理线程被优雅回收,不触发std::terminate(ND-1 修复) - select 输入类型全量校验 —
where/limit/offset类型不符抛清晰错误,不静默忽略(ND-4 修复) - uint64 溢出防护 —
_bind_value绑定前校验超出 INT64_MAX 抛错,防止静默数值截断(ND-5 修复) in运算符类型校验 — 非数组立即抛错,仅空数组保留恒假语义(ND-6 修复)
修复路线图
已完成
- P0-1: 列名白名单校验
- P0-2: 空
in生成恒假条件 - P1-1: 按 JSON 类型分别参数绑定
- P2-1: 清理时间戳格式统一为 T 分隔
- P2-2: 配置完整性必填校验
- P2-3 + P3-8: 所有数值配置项范围校验(port 1-65535、workers ≥1、cleanup_interval ≥1、retention_days ≥0、max_records ≥1)
- P3-1: schema 缓存基于
schema_version自动失效 - P3-2:
_coerce_valuenull 正确处理 - P3-3: limit/offset 非负校验
- P3-4: WHERE/IN 输入规模上限
- P3-5: 空 values 前置校验
- P3-6: 未知 WHERE 运算符报错
- P3-7:
utc_now()改用gmtime_r保证线程安全(第五轮) - P3-9:
columns数组元素类型检查,非字符串抛清晰错误(第五轮) - P3-10:
DbConn析构改用sqlite3_close_v2(第五轮) - P3-11:
safe_stoi包装所有std::stoi调用,带字段名友好错误(第五轮) - P3-12:
_log_internalcatch 块记录 stderr(第五轮) - P3-13:
_do_cleanupCOUNT prepare 失败记录 stderr(第五轮) - P3-14:
order_by非法元素抛错而非静默跳过(第五轮) - P3-15:
action非 string 抛错而非默认 "select"(第五轮) - P3-16:
order_by的column/desc字段类型校验(第五轮) - ND-1:
start()清理线程 RAII 守卫,rpc.start()异常路径不std::terminate(第六轮) - ND-4:
where/limit/offset类型不符立即抛错,不静默忽略(第六轮) - ND-5:
_bind_valueuint64 超 INT64_MAX 抛错,防止截断溢出(第六轮) - ND-6:
in运算符非数组值抛错,仅空数组保留恒假语义(第六轮)
待修复
无 — 全部 26 个问题已修复(100%)。新增 2 项已知限制(ND-2/ND-3)属兼容性设计决策,记录于"第六轮审计新增问题"章节。
审计方法
初次审计(2026-08-06)
- 通读
db2/src/db2.cpp全部 851 行实现 - 逐函数对照 Python 版
db/dbv2.py验证接口兼容性 - 审查 libmsg RpcServer 异常脱敏机制
- 逐注入点构造 PoC 验证 SQL 注入可行性
第一次复审(2026-08-07)
- 通读修复后代码 875 行
- 验证 P2-1/P2-2 修复
- 全量扫描发现 P3-5/P3-6
第二次复审(2026-08-07)
- 通读当前代码(main 分支 761cfd4)
- 逐函数验证所有 11 个已修复问题无回退
- 检查所有
std::stoi/数值转换路径,发现配置无范围校验问题(P2-3、P3-8、P3-11) - 检查所有系统调用线程安全性,发现
gmtime()问题(P3-7) - 检查所有 JSON 访问路径的类型安全,发现
columns缺类型检查(P3-9) - 对照 libsqlite3 审计最佳实践,发现
sqlite3_close可改进(P3-10) - 更新行号引用至当前代码
P2-3 / P3-8 修复验证(2026-08-07,第四次复审)
- 在
load_config末尾(命令行参数解析之后)新增 5 项数值范围校验:port 1-65535、workers ≥1、cleanup_interval ≥1、retention_days ≥0、max_records ≥1 - 非法配置立即
fprintf带字段名的明确错误信息后exit(1),与已有配置必填校验风格一致 - 修复 error handler lambda 未使用的
this捕获,消除-Wunused-lambda-capture警告 g++ -std=c++20 -O3 -Wall -Werror编译通过,零警告零错误- 更新审计文档:修复进度、问题状态、安全机制清单、修复路线图、复审结论
第五轮复审(2026-08-07,本次)
- 通读当前代码全部 960 行,逐函数审查
- 修复 4 个遗留 P3 问题:
- P3-7:
utc_now()改用gmtime_r线程安全版本 - P3-9:
columns数组元素添加is_string()类型校验 - P3-10:
DbConn析构改用sqlite3_close_v2 - P3-11: 引入
safe_stoi(s, field_name)辅助函数,替换全部 9 处std::stoi调用
- P3-7:
- 新发现并修复 5 个 P3 问题:
- P3-12:
_log_internal的catch (...)改为catch (const std::exception& e)并记录 stderr - P3-13:
_do_cleanupCOUNT 查询 prepare 失败时记录 stderr,简化 finalize 逻辑为单一出口 - P3-14:
order_by非法元素(非 object、缺 column、类型不符)改为抛错而非静默跳过 - P3-15:
action非 string 类型时抛错而非默认 "select" - P3-16:
order_by的column/desc字段添加is_string()/is_bool()类型校验
- P3-12:
clang++ -std=c++20 -O2 -Wall -Werror -c编译验证通过,零警告零错误- 更新审计文档:修复进度(22/22)、问题状态、安全机制清单(26 项)、修复路线图、复审结论
第六轮复审(2026-08-07,本次)
- 通读当前代码全部约 1000 行,逐函数审查
- 核查 libmsg
RpcServer/MsgServer依赖行为,确认start()在 socket/bind/listen 失败时抛出异常 - 新发现并修复 4 个问题:
- ND-1 (P2):
start()异常路径清理线程未回收 →std::terminate()硬崩溃,引入CleanupGuardRAII 守卫 - ND-4 (P3):
where/limit/offset类型不符静默忽略 - ND-5 (P3):
_bind_valueuint64 超 INT64_MAX 截断溢出 - ND-6 (P3):
in运算符非数组值静默当作空数组
- ND-1 (P2):
- 记录 2 项已知限制 ND-2/ND-3(无结果集上限、无 WHERE 全表更新/删除,均属兼容性设计决策)
clang++ -std=c++20 -O2 -Wall -Werror -fsyntax-only与g++ -std=c++20 -O3 -Wall -Werror -c双重编译验证通过,零警告零错误- 更新审计文档:修复进度(26/26)、问题状态、安全机制清单(30 项)、修复路线图、复审结论
并发性分析
并发模型
RpcServer (N workers, 默认 4)
├── thread 1 → thread_local DbConn (sqlite3*)
├── thread 2 → thread_local DbConn (sqlite3*)
├── thread 3 → thread_local DbConn (sqlite3*)
└── thread 4 → thread_local DbConn (sqlite3*)
Cleanup thread → thread_local DbConn (sqlite3*)
每个 worker 和清理线程各自持有独立的 sqlite3* 连接(thread_local),通过 WAL 模式实现并发读写。
优势
| 机制 | 位置 | 效果 |
|---|---|---|
| WAL 模式 | L223 | 读不阻塞写、写不阻塞读 |
| per-thread 连接 | L337-341 | 无跨线程共享,零连接池锁 |
| busy_timeout=5000 | L226 | 写-写冲突自动等锁最多 5 秒 |
| 无 GIL | — | C++ 线程真并行 |
瓶颈与风险
- 写-写串行化 — SQLite 同一时刻只允许一个写事务,典型写入 300-800 TPS,对当前规模足够
- 无批量事务 — 每条请求独立 auto-commit,无原子批量操作(Python 版同)
- 清理与业务写竞争 — 大批量 DELETE 持锁数百毫秒可能阻塞业务写入
_schema_mtx全局锁 — 仅首次反射表结构时持锁,缓存命中无锁,影响极小
与 Python 版对比
| 维度 | Python db (dbv2.py) | C++ db2 |
|---|---|---|
| 并发模型 | N worker + 共享 engine | N worker + per-thread 连接 |
| GIL 限制 | 有 | 无,真并行 |
| 连接管理 | SQLAlchemy 连接池 | thread_local 独占 |
| 写冲突处理 | SQLAlchemy 自动重试 | busy_timeout=5000 |
复审结论
当前状态:六轮审计累计发现 26 个问题,全部已修复(100%)。P0/P1/P2 级问题在第六轮后全部修复(2 P0 / 1 P1 / 4 P2),第五轮修复了 9 个 P3 问题,第六轮新发现修复 1 个 P2(ND-1)与 3 个 P3(ND-4/ND-5/ND-6)。SQL 注入防护机制完整,配置误操作风险已全部消除,所有 JSON 字段访问路径均有类型校验,异常路径均有错误日志,启动异常路径不再触发进程硬崩溃。新增 2 项已知限制(ND-2/ND-3)属兼容性设计决策。
第六轮修复亮点:
- 启动健壮性:
CleanupGuardRAII 守卫确保rpc.start()无论返回还是抛出都回收清理线程,消除异常路径std::terminate()硬崩溃 - 输入类型完备:
where/limit/offset类型不符立即抛错,杜绝"看似生效实则未生效"的静默偏差 - 数值保真:
_bind_value对超 INT64_MAX 的 uint64 抛错,防止静默截断溢出 - 运算符严格化:
in运算符非数组值抛错,仅空数组保留恒假语义(对齐 Python 版)
第五轮修复亮点:
- 线程安全:
utc_now()改用gmtime_r,消除多线程并发写日志时的数据竞争 - 资源安全:
sqlite3_close_v2替代sqlite3_close,异常路径不泄漏连接 - 错误可观测:
_log_internal和_do_cleanup不再静默吞异常,均有 stderr 输出 - 输入校验完整:
action/columns/order_by全部 JSON 字段类型校验,类型不符抛清晰错误 - 配置友好:
safe_stoi包装全部 9 处std::stoi调用,失败时带字段名定位
与 Python 版对比:安全基线已超越 Python 版——C++ 版新增了 schema 版本感知自动失效、空 values 校验、未知运算符报错、limit/offset 非负与类型校验、WHERE/IN 输入规模上限、表名格式校验、日志截断、配置范围校验、线程安全时间函数、安全 sqlite3 关闭、全量 JSON 字段类型校验、异常可观测、启动异常路径 RAII 守卫等防护,Python 版尚未实现这些。
生产可用性:✅ 可用于生产。全部 26 个问题已修复,30 项安全机制就位,核心安全与健壮性均有保障。2 项已知限制(ND-2 无结果集上限、ND-3 无 WHERE 全表更新/删除)为兼容性权衡,建议在调用方(gateway)侧做相应防护。