六、低危与提示(30 项)
| 编号 | 问题 | 位置 | 影响与修复要点 |
|---|---|---|---|
| 低 | /admin/database/convert 幽灵路由,必现 500 已实测 | index.php:195-196 | 路由指向不存在的方法(DatabaseController 仅 index/overview/settings/engine 四个 public)。任何访问触发 \Error → 500。修复:删除这两条路由。 |
| 低 | init.php 常量重定义 → 后台「部署路径」设置永不生效 + 每请求 Warning 已实测 | app/init.php:104-110 vs config.php:82 | config.php:82 已 define('BASE_PATH',''),init.php:108 再 define('BASE_PATH',$bp) 必失败(PHP 8 Warning: Constant already defined)→ 设置项实际从未生效,反而每请求产一条 Warning。修复:if (!\defined('BASE_PATH')) \define(...),或 config.php 用 defined()||define() 包裹。 |
| 低 | GET 即写操作且无 CSRF(3 处) | ThemeController.php:143-165、NotificationController.php:36-37、Admin/MaintenanceController.php:58-79 | <img src="…/layout-toggle?next=/"> 可改写受害者 list_excerpt;<img src="…/notifications"> 让全部通知变已读(重要提醒静默丢失);rebuild_pending 分支显式跳过 CSRF 且 GET 可触发全站计数重算。修复:改 POST + Csrf::verifyOrDie;pending 文件加 TTL 与一次性 token。 |
| 低 | 用户 status 写入无白名单 | Admin/UserController.php:32,63-66、Models/User.php:469 | 参数化绑定无 SQL 注入,但 status 可写成任意字符串('ACTIVE'、空串)→ 登录判定严格等值时造成「永不可登录」。查询侧已有 ['active','banned'] 白名单(User.php:126),写入侧缺失。修复:写入前同一常量白名单化。 |
| 低 | 内置用户组(1~4)可被重命名,与注释矛盾 | Admin/GroupController.php:34-42、Helpers/Permission.php:300-303 | 注释声明「不允许改名」,但两处只钳制 is_default,$name 照常写库 → 可把组 4(管理员组)改名为「普通用户」制造权限语义混淆。修复:内置组拒绝改名并回显错误。 |
| 低 | 归档桶并非只读;归档脚本与写入存在丢数据竞态 | Schema.php:868-896、Thread.php:376-429,600-606,704-706、cli/archive.php:111-195 | 数据层无只读约束,管理员编辑老帖会直接改写 archive 桶。更严重:归档脚本在 VACUUM INTO(:123) 之后、翻索引(:169) 之前若有回帖写入源桶,随后源桶被删(:192) → 回复永久丢失。修复:bucketFromPath 检出 archive 前缀时用 PRAGMA query_only=1;归档前对目标季度置「冻结标记」,或改为「先翻索引再删源桶」。 |
| 低 | 删除/迁移类操作无事务,部分失败留下分裂态 | Models/Category.php:232-267、SplitDB/SearchIndexStore.php:235-241,246-256,348-353 | Category::deleteWithReset 逐桶 UPDATE 中途异常 → 部分桶已改、索引未改、版块未删;搜索索引删除逐季度循环,任一被锁 → 帖子已软删但仍可搜到且无重试。修复:接入补偿日志 / task_queue 重试;循环内 per-quarter try/catch 保证「能删的都删」。 |
| 低 | 活跃桶缺失时静默新建空库(归档桶已修,活跃桶未修) | SplitDB/Schema.php:877-895 | :878-885 归档桶缺失会抛异常;:886-892 活跃桶缺失则静默建目录+建表 → 读取返回「无此帖」(404 而非故障告警),且新数据写入「新生」桶而历史帖子永久失联。修复:区分「索引指向的桶应存在」与「新帖首次写入」,前者缺失时同样抛异常 + error_log。 |
| 低 | data/ 下存在历史路径拼接 bug 的残留目录 已实测 | 实测目录(非代码行) | data/bucket/active/bucket/active/2026Q3/ 有 18 个 sqlite(桶文件总数 50 = 32 正确 + 18 嵌套),mtime 2026-08-27,属旧版本以 data/bucket/active 作 DATA_PATH 调用的产物。会污染桶扫描/备份/统计类工具。修复:比对 topic_index.bucket_path 全集确认无引用后归档;并加启动自检「磁盘桶文件集合 ⊇ bucket_path 集合」。 |
| 低 | 模型层静态装饰缓存无上限(CLI 常驻进程 OOM 风险) | Models/Thread.php:32-35,877-891、Models/User.php:352,234 | php-fpm 下每请求重置无影响;但 cli/worker.php 常驻消费 rebuild_search 会不断累积 uid/cid 条目,长期运行内存单调增长。对照 Post::$threadIndexCache 已有 500 条上限(Post.php:361,375-377)。修复:给两处加同样的 FIFO 上限与淘汰。 |
| 低 | IDGenerator 在池化连接上永久改写 busy_timeout | SplitDB/IDGenerator.php:57-61 | $db->exec('PRAGMA busy_timeout = 1000') 对池化连接持久生效(原统一值 5000ms),缩短了该连接上其他用途的容忍窗口。修复:取号结束后恢复 5000;或为 global_id 使用不经池共享的独立连接。 |
| 低 | 验证码降级盐固定 / PoW 难度过低 / /api/captcha 无限流 | Helpers/Captcha.php:30-33、Helpers/PoW.php:24,80-87、ApiController.php:440-451 | DB 异常时 captcha 盐为 hash('sha256','FlintHubCaptcha_'.__DIR__) 固定值(注释却称「基于时间」)→ 32^4 组合可离线枚举。PoW 默认难度 4 = 16 bit,本机实测 <0.1s,且 /api/pow 无限流。图片验证码仅在 PoW 失败时启用(PoW 几乎总被通过),/api/captcha 无 RateLimiter 可做资源放大。修复:回退盐改为进程内随机存 session;/api/captcha 加限流;触发条件改为「IP 近期失败次数超阈值」。 |
| 低 | Csrf::verify 不校验时效 + 数组传参 TypeError | Helpers/Csrf.php:16-28、ThreadController.php:92、AuthController.php:61,73,75 | verify 只做 hash_equals 不读 csrf_time,「30 分钟有效」在验证侧未生效(token 可长期复用);反之轮换会让长开表单/多标签页误判 403。csrf[]=1 等数组传参触发 TypeError → 500(非绕过,但可被单参数打成 500 污染日志)。修复:verify 内补时效判定;调用侧统一取标量 is_string($_POST['csrf'] ?? null) ? ... : ''。 |
| 低 | 私信/通知分页页码未按真实总页数钳制 | MessageController.php:46-48,76-78、NotificationController.php:39-43 | 仅做下界钳制,?page=99999999 生成超大 OFFSET → SQLite 需扫描计数,可低成本放大单请求开销;越界页返回空白而非末页。修复:先 COUNT 再 $page = max(1, min($totalPages, $page)),与 ForumController.php:80 一致。 |
| 低 | 修改邮箱无唯一性预检 → 500 + 邮箱占用枚举预言机 | ProfileController.php:39-45 | users.email 为 UNIQUE(Schema.php:274),改成他人邮箱抛 PDOException → 通用 500;成功/500 的差异构成占用枚举。且邮箱变更后 email_verified 仍为 1,不重走验证。修复:先 findByEmail 查重再更新,并按需重置验证状态。 |
| 低 | /resend-verification 端点实际不可达 | index.php:154、AuthController.php:465-471 | 仅注册了 GET 路由,而方法内要求 POST → GET 被自身重定向到 /login,POST 因 Router 方法不匹配 404。重发验证邮件功能整体失效,邮箱验证中断后用户无自助恢复路径。修复:补 $router->post('/resend-verification', ...)。 |
| 低 | PostController::create 使用未定义变量 $categoryId | PostController.php:84(赋值在 :88) | 正文超长分支触发 PHP 8「Undefined variable」Warning,表单回显分类丢失;若开启 display_errors 会泄露绝对路径。修复:把 $categoryId 赋值上移到与 $title/$content 同批。 |
| 低 | 站点名双重转义(& 显示为 &) | Core/Controller.php:31-32 + Core/TemplateCompiler.php:291-294 | $defaults 中已 htmlspecialchars,视图侧 $this->e() 再转一次。站点名含 &/' 时显示为实体。修复:$defaults 不再预转义,统一由视图负责。 |
| 低 | CSP 含 unsafe-inline + unsafe-eval,且缺结构性指令 | Core/Controller.php:77 | script-src 同时放开内联与 eval → CSP 对 XSS 的缓解能力归零(这也是 S-1 与 H-4 可直接执行的原因)。未声明 object-src 'none'、base-uri、frame-ancestors(仅靠 X-Frame-Options)。修复:至少补齐 object-src 'none'; base-uri 'self'; frame-ancestors 'self'。 |
| 低 | 首页前置缓存键与控制器写入键不一致 → 前置缓存对首页 100% 失效 已实测 | index.php:62-70 vs HomeController.php:32 | index.php 用 'home_' . $page(=home_1),控制器写 home_1_c/home_1_p → 永不相等。实测 data/runtime/pages/ 只有 home_1_c_e1l180.html,无 home_1_* 前缀文件。index.php 为首页付出的引导开销全部白费。修复:首页分支改为 Theme::getHomeMode() === 'community' ? 'home_1_c' : 'home_1_p'。 |
| 低 | Settings 文件缓存对「值变更」不做对账 已实测 | Helpers/Settings.php:105-134,145-177 | reconcileWithDb 只比对键名集合(:139-140 注释自认),同键不同值(最常见:用备份还原 business.sqlite)不触发重建 → 后台显示新值、前台仍用旧值。实测 base_path 设置当前为 ''。修复:额外比对 COUNT(*) 或键集合哈希,不一致即全量重建(60s 节流,成本可接受)。 |
| 低 | 备份路径前缀比较缺目录分隔符 / zip 导入不校验内容 | Admin/DatabaseController.php:995-1000、:747-774,855-860 | strpos($realTarget, $realRoot) !== 0 未加分隔符 → 同级 data2/database 目录可越出。zip 分支拒绝 ../绝对路径(Zip Slip 已防),但不校验内容与扩展名,可把任意文件名/内容写进 data/(.php、.htaccess),当前靠 data/.htaccess 与 nginx 规则阻断。修复:前缀比较改为 $realRoot . DIRECTORY_SEPARATOR;zip 仅允许白名单后缀。 |
| 低 | Upload 的 Imagick「格式白名单」写法无效 | Helpers/Upload.php:162-165 | setOption('svg:auto-graphic','false') 不是 ImageMagick 的 coder/策略开关,无法阻止按内容自动识别格式。当前被上层 MIME 白名单挡住(image/svg+xml 不在 $allowedMimes),属纵深防御失效而非可利用;一旦有人把 svg 加进白名单即成 RCE 面。修复:用 setResourceLimit 并在 policy.xml 禁用 MSL/MVG/SVG coder。 |
| 低 | UserPrefs 无失效入口;PageCache 落盘非原子 | Helpers/UserPrefs.php:29-69、Helpers/PageCache.php:119-137,97-114 | UserPrefs 请求级缓存一旦填充本请求内永不失效(当前 ThemeController 走 redirect 侥幸避开);PageCache 直接 file_put_contents 目标文件(对比 Settings/Plugin 用 tmp+rename),76KB 级 HTML 写入中被并发读者读到会输出截断页面(无 500、无告警)。修复:补 UserPrefs::clear();render 改 tmp+rename。 |
| 低 | Tag 保存无数量上限 / 无去重;tags.name 无唯一约束 已实测 | Helpers/Tag.php:64-96、SplitDB/Schema.php | 单次发帖可提交上万标签 → 上万次 SELECT/INSERT(同步请求内,可拖慢站点);重复标签名做重复查询。实测 sqlite_master:tags 只有非唯一索引 idx_tags_name(thread_tags 的 uk_thread_tags 正确)→ 并发新建同名标签产生重复行。修复:array_slice(array_unique(...), 0, 10);改 INSERT ... ON CONFLICT(name) DO NOTHING 后回查 id;补 UNIQUE(tags.name)。 |
| 低 | Plugin::hook 用 include(非 include_once)+ EXTR_SKIP 作用域污染 | Helpers/Plugin.php:448-486(受影响 plugins/user_level/hook/route_before_dispatch.php:46) | ① 同一钩子文件在一次请求中被 include 两次时,若其中声明函数/类 → Cannot redeclare 致命错误(E_COMPILE_ERROR,:480 的 try/catch 拦不住)。nav_plugin_links 在一次页面渲染中被调用两次(layouts/main.php:212 + left_sidebar.php:100)且已被 10+ 插件挂钩。② EXTR_SKIP 使上个插件创建的变量「粘」给下个插件,存在隐式作用域污染。修复:改 include_once 并规定钩子文件禁止声明全局符号;把每次 include 包进独立 closure 作用域。 |
| 低 | Theme::getCurrent() 的 default_theme 分支未做可用性校验 | Helpers/Theme.php:184-194、消费点 Core/TemplateCompiler.php:39-45 | cookie 与 ?preview_theme= 两条路径都过 getAvailable() 白名单,唯独 default_theme 直通。若该设置被写入 ../..(与 H-7 组合可达)→ themePath 可能命中站点根,模板解析可加载根目录 .php。修复:if ($default !== '' && isset(self::getAvailable()[$default])) return $default;,并对 $current 加 ^[A-Za-z0-9_-]+$ 白名单。 |
| 低 | SessionHandler 未实现 validateId → use_strict_mode 完全失效;read() 吞异常 | Core/SessionHandler.php:24,58-60、config.php:47 | PHP 只有在处理器实现 SessionUpdateTimestampHandlerInterface(validateId)时才能校验 SID 是否存在,否则一律接受 → config.php:47 的 use_strict_mode=1 与注释不符,会话固定原语始终可用(登录路径的 session_regenerate_id(true) 是唯一缓解)。另 read() 把「库不可用」与「会话不存在」折叠为同一个 '' 且完全不记日志。修复:实现 validateId + updateTimestamp(注意确认 TTL 续期路径仍在,避免引入 30 分钟硬过期回归);read 的 catch 中补 error_log。 |
| 低 | 登录失败路径存在可测量的账号枚举时序差异 | AuthController.php:81-87 | 用户不存在时额外执行一次 password_hash(+一次 password_verify),比「用户存在但密码错」多约一个 bcrypt 周期(数十 ms)。usleep(250000) 对两者都执行,无法掩盖该差异。修复:把 dummy hash 预生成为常量,使两条路径计算量一致。 |
| 低 | 生产未关闭 display_errors(PHP 8.4 下会回显弃用告警与绝对路径) | config.php:84-85 | 全仓库检索 display_errors 仅命中安装脚本,运行时配置从未关闭。项目基线 ≥ 8.0 但生产跑 8.4+,弃用/警告会连同绝对路径输出到页面顶部(JSON/AJAX 场景直接解析失败),且 index.php:292-300 的 try/catch 覆盖不到 Warning/Deprecated。修复:config.php 末尾补 ini_set('display_errors','0'); ini_set('log_errors','1');(保留 E_ALL 但关回显,让弃用信息进 error.log 以便清理)。 |
| 低 | Database::closeAll() 无法真正释放业务连接;begin/commit 无嵌套保护 | Core/Database.php:53-59,130-143,186-189 | closeAll() 只清 DBFactory::$connections,Database::$instance->db 仍持强引用 → PDO 引用计数不归零,连接不释放;$instance 与静态计数器也不重置。Admin/DatabaseController.php:675,767 在库切换流程中调用它,期望「断开后重连」,实际拿到仍指向旧库的句柄。另 begin() 直接委托 PDO::beginTransaction(),嵌套调用抛异常且无法用 savepoint 语义处理。修复:closeAll() 内清空实例并重置计数器;begin() 加 inTransaction() 守卫。 |
| 低 | json() 在响应头已发出后可能抛异常 → 畸形响应 | Core/Controller.php:84-92 | 非法 UTF-8 数据让 json_encode 抛 JsonException,被全局兜底捕获后输出 <h2>500 Internal Server Error</h2>,而 Content-Type 已是 application/json → 前端 .json() 解析失败且错误信息为 HTML,排查困难。修复:去掉 JSON_THROW_ON_ERROR,改用 JSON_INVALID_UTF8_SUBSTITUTE,或先判定再降级输出 {"error":"encode_failed"}。 |
| 低 | 防开放重定向漏检反斜杠 + 信任客户端可控的 HX-Current-URL | Core/Controller.php:97、Helpers/Auth.php:104-107,141 | 三处 guard 只挡 // 不挡反斜杠,实测对 /\evil.com 放行;浏览器按 WHATWG URL 规范把特殊协议下的 \ 归一化为 / → 等价于协议相对跳转。且 Auth.php:104-107 优先取完全可控的请求头 HTTP_HX_CURRENT_URL。init.php:71-75 的同款检查已含反斜杠判定,说明此处属遗漏。修复:三处统一为同一校验函数(拒绝 \\、\r、\n、//),并移除 HX-Current-URL 分支或对其套用同一校验。 |
| 低 | sw.js 的 CACHE_VERSION 与版本号脱节 | sw.js:13 vs version.php:7 | flinthub-static-v1.0.1 vs V1.0.8 —— 两者已不同步,说明「升版时改 sw.js」这条纪律已出现漏执行。sw.js 对 css/js 采用 Cache-First,一旦改 modern.css 忘记递增,老访客将持续使用旧样式(与 PageCache 的 HTML 缓存叠加,排查困难)。修复:由版本号生成 —— 改 sw.php 或构建时替换占位符。 |
七、修复优先级路线图
P0 — 立即修(可在数小时内完成,止血)
- S-1 净化器删除
style/script等 raw-text 元素 + CDATA 输出转义 → 堵住存储型 XSS。这是唯一「在线可被利用」的严重漏洞,应最先处理。 - H-4 搜索页
type白名单 +pagination()对 href 转义 → 堵住反射 XSS。 - H-3
/api/upload与/api/fetch-image改为默认拒绝 → 堵住零权限上传。 - H-2
Auth::getCurrentUser()补status判定 + RememberMe 复用同一判定 → 让「封禁」真正生效。 - M-1 ViewCounter 探测
apc.enabled→ 立刻止住 error.log 每请求刷屏(已 178 条)。 - M-3 轮换
MAIL_PASS_KEY/CRON_KEY默认值并改为随机生成 → 已在线的站点应立即更换。 - 低危-幽灵路由 删除
/database/convert两条路由(1 行改动,消除必现 500)。
P1 — 本迭代修(需要设计,影响数据正确性)
- H-1 引入补偿日志(wal_journal)或最小可行的「顺序 + 失败补偿」→ 消除不可达帖子与孤儿正文。
- H-5 计数器:补 status 过滤、把
reply_count维护下沉到模型、修正runtimeGet的零值自校正 → 修复已实测的漂移并防复发。 - H-8 ExternMerger 复用同一把锁 + 写
.idx后统一失效 APCu → 消除编辑静默失效与正文空白。 - H-6 后台审计补全(User / Plugin / Group / Trashed / Backup)+ 结构性覆盖自检。
- H-7
Setting::setValue()下沉权限校验(调用栈反查)→ 让插件权限强制模式真正生效。 - M-2 RateLimiter 临界区合并 + 文件裁剪 → 恢复限流在高并发下的有效性。
- M-5 / M-7 / M-9 / M-10 / M-11 Model 层白名单化、权限默认拒绝、改密全端登出、积分去重、归档判定下沉。
P2 — 计划修(性能、健壮性、一致性)
- M-6 缩短回帖事务、把插件 hook 与上传移出临界区。
- M-8 / M-12 / M-13 / M-14 / M-15 / M-16 服务端长度校验、索引优化(须先做查询计划回归)、队列租约、LIKE ESCAPE、通知计数列、DDL 进程内标记。
- 低危全量:GET 写操作改 POST、
init.php常量守卫、内置组改名限制、归档桶只读、残留目录清理、缓存键与sw.js版本同步、display_errors关闭等。
回归验证建议(务必执行):
- 修复 S-1 后,把本报告中的 4 组载荷加入净化回归集,断言输出不含
onerror/onload/<script; - 修复 H-5 后,跑「维护 → 重建统计」并用附录脚本断言
_runtime_total_*==COUNT(*); - 新增索引(M-12)必须在生产规模副本上
EXPLAIN QUERY PLAN回归,确认未出现新的USE TEMP B-TREE且未违反「topic_index不得有 status 打头索引」红线; - 修复 H-8 后,模拟「合并进行中并发编辑」,断言编辑结果未被覆盖、读端正文非空。
八、已确认的良好实践
以下均为逐行回读确认,带行号佐证 —— 这套系统的安全水位在同类自研项目中明显偏高,修复时应保留这些设计:
安全基线
buildWhere是真正的 fail-closed 白名单解析器:字符串分支逐段正则校验(仅允许列 比较符 :占位符以 AND 连接,拒绝 OR/IN/子查询/字面量/函数),数组分支对空数组与全非法键抛异常而非退化成全表匹配(Core/Model.php:181-210)。- 数据库异常不泄露:底层
PDOException只写error.log(含完整 SQL),对外统一抛通用RuntimeException(Core/Database.php:98-106)。 - 密码学用法规范:
password_hash(PASSWORD_DEFAULT)无自研散列;登录/改密/自动登录三处session_regenerate_id(true);全站hash_equals;RememberMe 只存 sha256 且一次性轮换(RememberMe.php:60-88)。 - Mailer GCM:12 字节随机 IV + 独立 16 字节 tag +
FHGCM\x01魔数版本前缀,认证失败拒绝且不回退旧格式(防降级);verify_peer/verify_peer_name均为 true,显式限定 TLSv1.2/1.3(Mailer.php:41,52-66,184-192)。 - Upload 三重校验:扩展名白名单 +
strtolower归一 +\0拒绝 + 无条件finfoMIME 二次校验;MIME 映射表独立于可配置扩展名 —— 即使后台把 php/phtml/svg 加进白名单也会被拒(Upload.php:39-81)。 - SSRF 防护较完整:内网/CGNAT/云元数据段黑名单 + DNS 解析后二次校验真实 IP + 以解析 IP 替换 host 防 DNS Rebinding + 逐跳重定向每跳复检(
ApiController.php:197-230,285-298,399-438)。 - CSRF:
random_bytes(32)生成 +hash_equals比较 + 登录/登出显式轮换 + 敏感操作一次性消费(Csrf.php:17,27,44,53)。
工程与架构
- 缓存统一 tmp+rename+
opcache_invalidate原子写(Settings.php:203-212、Plugin.php:366-386、Points.php:86-95),杜绝半截缓存。 Settings::loadCacheFile()用json_decode替代@require,消除缓存文件 RCE(Settings.php:110-115)。- 插件钩子路径做
realpath收容 + 追加目录分隔符,防兄弟目录绕过(Plugin.php:466-473)。 - Autoloader 路径穿越双重防护:
..显式过滤 +realpath前缀包含校验(Autoloader.php:19-29,54-64)。 - 分片引擎:
IDGenerator用BEGIN IMMEDIATE+ SELECT/UPDATE 等价RETURNING保证取号原子性(:68-98);搜索索引每 150 词条分块 INSERT,彻底规避 SQLite 默认 999 变量上限(SearchIndexStore.php:204-223);扩容零迁移成立(ShardRouter.php:84-102)。 - 索引红线被严格遵守:实测
PRAGMA index_list(topic_index)无 status 打头索引,所有索引 DDL 均在CREATE TABLE之后(Schema.php:766-775)。 - 幂等补列/补索引有 marker 门控,命中即短路,消除每请求
PRAGMA table_info固定税(Schema.php:635-697)。 DBFactory连接后校验journal_mode实为 WAL 并对回落告警(DBFactory.php:77-82)。AuditLog:XFF 仅在直连 IP 属受信代理网段时采信,否则恒记REMOTE_ADDR,防伪造审计 IP 嫁祸(AuditLog.php:35-84);日志保留 90 天由 cron 清理,未暴露任何后台 HTTP 删除入口。- PageCache 已按主题/语言/摘要偏好三维分键,且
serve/render均被!Auth::isLoggedIn()硬门禁,登录态不进入静态缓存(PageCache.php:42-90)。 - 三语语言包键集合完全一致(实测 zh/en/zh_tw 各 1296 键,缺失 0)。
- PHP 8.5 弃用项已主动清理(
finfo_close()/imagedestroy()已删并留注释,Upload.php:58,221,231,242)。
九、附录:验证方法与复现脚本
本次审计的所有实证均为只读:未写入任何业务数据、未运行任何破坏性命令(未执行 bench_write / 重建统计 / 修复工具)。探针脚本位于 .workbuddy-ai/tmp/,可直接复用:
| 脚本 | 用途 |
|---|---|
verify_xss.php | 复现 S-1:向 Content::sanitizeHtml() 与 formatPostContent() 投喂 4 组 style/script 载荷,打印实际输出 |
verify_expr.php | 复现 M-4:反射调用 safeExpr() 与 compile(),验证 {if isset($x) && system("id")} 的编译产物 |
verify_drift.php / verify_drift2.php | 复现 H-5:只读比对 _runtime_total_* 与真实 COUNT(*),并逐主题比对 reply_count |
verify_plan.php | 复现 M-12:对 topic_index 跑 EXPLAIN QUERY PLAN 并导出索引清单与定义 |
运行方式:
cd /d/phpstudy_pro/WWW
/d/phpstudy_pro/Extensions/php/php8.2.9nts/php.exe .workbuddy-ai/tmp/verify_xss.php注意:data/meta/main_index.sqlite 是 WAL 模式 —— 若要拷副本分析,必须连同 -wal / -shm 一起拷,只拷单文件会读不到表。本次实证均直接在原库上跑只读 SELECT。
与既有审计产物的关系
仓库内已有 app/SplitDB/SplitDB_Code_Audit_Report.html(2026-09-02,SplitDB 专项)。本次复核结果:该报告的 18 条结论中 11 条已修复(如跨季度编辑 .idx 失效、巨型 INSERT 变量上限、closeAll 死代码、队列清理、countTargets 全量入内存等),4 条已变化或引入新缺陷(如 APCu TTL 已加但 flush 并发重复落库),3 条仍存在(read 端 APCu 与 write 竞态、三份真相一致性依赖调用方、data/ 防护依赖部署配置)。本次报告在此基础上扩展到 app/ 全量非 Views 代码,并新增了跨库事务、计数器漂移、封禁失效、审计缺失等此前未覆盖的结论。