[审计]FlintHub 插件审计报告

👑Lv.11 元老 🌞 骨干会员
2026-10-25 00:32:24

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/docs/插件审计报告.md。

FlintHub 插件审计报告

项目内容
审计对象plugins/ 下全部插件(28 个目录)
审计依据docs/插件开发规范.md v1.3(2026-09-20) —— §一 三条硬性规则、§一五 八条最高级别红线、§二~§十一 结构与红线清单
审计方式静态代码审计(词法预处理 + 规则匹配)+ 关键点源码逐条人工复核 + PHP 语法校验 + 钩子双 include 回归
审计时间2026-09-21
覆盖规模28 个插件 / 390 个 PHP 文件 / 23 个双触发钩子文件
扫描器.workbuddy-ai/tmp/audit2.py(原始结果 audit2.json)、.workbuddy-ai/tmp/lint_plugins.php、.workbuddy-ai/tmp/reinclude_test.php


一、结论摘要

没有发现「一票否决」级的安全漏洞(无反序列化/动态执行、无 SQL 注入、无路径穿越、无 XSS 输出绕过、CSRF 覆盖率 100%)。

问题集中在 1 个功能性致命 BUG、积分/用户组绕过核心封装、以及主题变量化改造不彻底三类。

级别确认问题数涉及插件性质
P0 致命221 个功能 100% 不可用 + 1 个零内联红线
P1 高危/规范12118核心库直写 6、主题硬编码 105、内联 5、i18n 1、结构 3、死钩子 1
P2 改进11917旧变量/主色硬编码、直查核心表、风格偏差等
合计24220——

扫描器原始命中 285 条,经逐条源码复核剔除 43 条误报(占位符变量 {$ph}、注释内关键词、var(--mn-*, #fff) 合法 fallback、PDO::quote() 已转义、白名单 in_array 守卫等),下表为复核后确认值。

最需要立刻处理的两件事:

  1. plugins/invite/InviteController.php:46 —— 邀请码生成功能 100% 报 500($db->begin() 调用了不存在的方法)。
  2. plugins/user_level/views/admin.php:273 —— 后台按钮使用内联 onclick,违反零内联 P0 红线。

二、审计范围与方法

2.1 检查维度(对应规范章节)

维度规范依据检查内容
目录与元数据§二、§三、§11.3一目录一插件、残留文件、plugin.json 合法性与必填字段、hooks 路径存在性与分隔符、permissions 声明、plugins_cache.json 一致性
安全红线§一、§一五、§11.1反序列化/动态执行、绝对路径、渲染钩子中断、上传白名单、原子扣减、IN(...) 占位符、PDO 属性、AJAX CSRF、SQL 注入、路径穿越、开放重定向、IDOR
控制器§五基类继承、requireLogin()、404 处理、冗余 require_once、静态调用核心 Model
视图与资源§六、§七section() 组织、$this->e() 转义、$this->url()/BASE_PATH、零内联(<script>/on*/style="")、?v=filemtime
语言包§八三语文件齐备、键集合与键序一致、前缀规范、引用键 vs 字典全量比对
数据库§十独立库、核心表访问边界、事务写法、SQLite DDL 语法
钩子§九钩子名合法性、死钩子、双触发钩子顶层符号、渲染钩子约束、导航钩子字号约定
主题适配§11.7--mn-* 变量化、硬编码颜色、花括号配对
性能§11.4渲染钩子内同步外部 IO、每页 hook 查询

2.2 审计手法(可复现)

# ① 静态扫描(词法预处理:剥离注释与字符串,避免注释内关键词误报)
C:/Users/Administrator/.workbuddy-ai/binaries/python/versions/3.13.12/python.exe .workbuddy-ai/tmp/audit2.py

# ② PHP 语法校验(单进程 token_get_all + TOKEN_PARSE,等价 php -l,390 文件约 2s)
/d/phpstudy_pro/Extensions/php/php8.2.9nts/php.exe .workbuddy-ai/tmp/lint_plugins.php

# ③ 双触发钩子双 include 回归(§9.4 ③;必须带框架引导,否则类未加载会产生假失败)
/d/phpstudy_pro/Extensions/php/php8.2.9nts/php.exe .workbuddy-ai/tmp/reinclude_test.php

# ④ 顶层符号静态普查(§9.4 ①)
grep -rnE '^[ \t]*(abstract |final )?(function|class|interface|trait) [A-Za-z_]' plugins/*/hook/

方法学提示:规范 §9.4 ③ 给出的 php -r 'include $f; include $f;' 不带框架引导,实测会对 12 个正常钩子文件报 Class "app\Helpers\I18n" not found 之类的假失败(类未自动加载,与双 include 无关)。必须改为先 require app/Core/Autoloader.php 再 include,才测得出真实结论(本报告结论基于带引导版本)。


三、问题分级定义

级别定义处置要求
P0 致命违反 §一五 八条红线,或导致功能完全不可用立即修复,禁止发布
P1 高危/规范违反 §11.1~§11.3 明列红线;绕过核心封装;主题适配失效;残留/死钩子本迭代内修复
P2 改进风格偏差、可读性/一致性、潜在退化风险排期优化


四、P0 致命问题(2 条)

P0-1 🔴 invite 邀请码生成功能 100% 报 500

项内容
位置plugins/invite/InviteController.php:46
代码$db->begin();(其中 $db = self::db() → \app\Helpers\Plugin::db('invite'))
违反§10.2「$db->begin() 不存在——原生 PDO 只有 beginTransaction()」

证据链:

  1. InviteController.php:20-22 —— public static function db(): \PDO { return \app\Helpers\Plugin::db('invite'); } → 返回原生 PDO;
  2. InviteController.php:30 —— $db = self::db();;
  3. InviteController.php:46 —— 在 else 分支(即积分足够、即将生成邀请码)中调用 $db->begin();;
  4. 实测:
$ php -r '$p=new PDO("sqlite::memory:"); try { $p->begin(); } catch (\Throwable $e) { echo get_class($e).": ".$e->getMessage(); }'
Error: Call to undefined method PDO::begin()

影响:抛出的 Error 不是 Exception,InviteController.php:58 的 catch (\Exception $e) 捕不到 → 未捕获致命错误 → 用户每次点击「生成邀请码」都得到 500 页面(积分不足时因走 if 分支反而正常,掩盖了问题)。

修复建议:

// plugins/invite/InviteController.php:46
- $db->begin();
+ $db->beginTransaction();

// :58  catch 内同时补 inTransaction 守卫(见 P2 项 ROLLBACK_NO_GUARD)
  } catch (\Exception $e) {
-     $db->rollback();
+     if ($db->inTransaction()) { $db->rollBack(); }

同类排查结论:全仓 ->begin() 共 9 处,其中 app/Controllers/PostController.php:133、ThreadController.php:132、app/Helpers/Points.php:222、Permission.php:331、app/Models/Blog.php:124、User.php:609 以及 red_packet 的 3 处,接收者均为 app\Core\Database(该类确有此方法,Database.php:130 且带嵌套事务保护)→ 仅 invite 这一处是真实缺陷。


P0-2 🔴 user_level 后台按钮使用内联 onclick

项内容
位置plugins/user_level/views/admin.php:273
代码<button type="submit" class="btn btn-secondary" onclick="return confirm('<?php echo $this->e($this->t('plugin.user_level.backfill_confirm')); ?>');">
违反§7.3 P0 级第 2 条「禁止内联 on 事件属性,一律在 assets/script.js 内 addEventListener 绑定;传参走 data- 属性」

影响:该行文本虽已用 $this->e() 转义(无 XSS),但内联事件属性违反零内联红线,且受 CSP script-src 策略限制时确认弹窗会失效。

修复建议(对齐 announcements / medal / content_review 的既有整改方式):




// assets/script.js
document.querySelectorAll('[data-ul-confirm]').forEach(function (el) {
    el.addEventListener('click', function (e) {
        if (!confirm(el.dataset.ulConfirm)) e.preventDefault();
    });
});


五、P1 高危 / 规范问题(121 条)

5.1 核心库直写 —— 绕过 Points / User 封装(6 处)

违反 §11.8-1「禁止直写核心库用户/积分表」。绕过封装会同时丢失:积分日志(points_log)、等级重算、用户缓存失效、points_award_after / points_deduct_after 钩子。

插件位置问题影响
daily_checkinCheckinController.php:218UPDATE users SET points = COALESCE(points,0) + :points且全插件未写 points_log → 签到积分无任何日志、无等级联动、无钩子
quiz_duelQuizDuelController.php:307UPDATE users SET points = points + :p(发奖)手工补写 points_log,但等级/缓存/钩子缺失
quiz_duelQuizDuelController.php:327UPDATE users SET points = points + :p(退款)同上
red_packetRedPacketController.php:311UPDATE users SET points = points + :p(抢红包)同上
red_packetRedPacketController.php:421UPDATE users SET points = points + :p同上
points_mallPlugin.php:508(new \app\Models\User())->execute('UPDATE users SET group_id = :gid ...')虽经 Model 转发,但仍是裸 SQL,未走 User::updateGroupId()(§11.8-1 明列 mod_system 同款写法曾被审计标记)

修复建议:

// 积分增加
\app\Helpers\Points::award($userId, $points, $reason, $refId, $refType);
// 积分扣减(原子、防负)
\app\Helpers\Points::deduct($userId, $points, $reason, $refId, $refType); // 返回 bool
// 用户组变更
(new \app\Models\User())->updateGroupId($uid, $groupId);

quiz_duel:304 注释自述「避免 Points::award 的级别计算可能导致异常」——属已知取舍,但应按规范改为「Points::award 外层 try/catch 兜底」而非绕过封装;invite:54 用 Points::award($userId, -$pointsCost, ...) 传负数扣分,应改用 Points::deduct()。

5.2 主题硬编码颜色 —— 暗夜星辰主题下失读(105 处 / 15 个插件)

违反 §11.7。这是本次审计数量最多的一类问题:核心主题只定义 :root 变量,不为插件类补样式,硬编码色值在深色主题下表现为「白卡片 + 浅色字」不可读。

插件白色背景深色文字小计
red_packet72027
icon_generator101525
code_repo71017
medal235
friend_links235
seo044
announcements224
ai_assistant303
content_review303
mod_system123
forum_required_tag202
post_favorite022
qa022
invite011
user_profile101
xiuxian101
合计4164105

典型样本:

/* plugins/announcements/assets/style.css:34 */
.fl-modal { background:#fff; ... }
/* plugins/announcements/assets/style.css:38 */
.fl-modal-field label { ... color:#555; }
/* plugins/code_repo/assets/style.css:57-60 */
.repo-btn-ghost { background:#fff; color:#556; border:1px solid #d9dee8; }
/* plugins/code_repo/assets/style.css:65 */
.repo-btn-disabled { color:#999; }

修复模板(fallback 值 = 浅色主题下的目标色,保证浅色零副作用):

.fl-modal        { background: var(--mn-bg-card, #fff); }
.fl-modal-field label { color: var(--mn-text-secondary, #555); }
.repo-btn-ghost  { background: var(--mn-bg-card, #fff); color: var(--mn-text-secondary, #556); border: 1px solid var(--mn-border, #d9dee8); }
.repo-btn-disabled { color: var(--mn-text-muted, #999); }

验证(§11.7 发布前必做):

grep -nE 'background: *#fff|color: *#[0-9a-fA-F]{3,6}' plugins/{插件名}/assets/style.css   # 应零命中
# 实机切换「暗夜星辰」主题走查:卡片 / 文字 / 按钮 / 结果框 / 提示框

5.3 零内联违规(5 处)

插件位置问题规范条款
draft_saverhook/layout_head_end.php:31echo '<script>window.FlintDraftUserId=' . $userId . ';</script>';§7.3 P0-1(内联 <script> 块)
forum_required_tagviews/admin.php:143 / 171 / 181style="background:<?php echo $this->e($_c); ?>;" 动态色值直接写 background 属性§7.3 P1-4(应改 CSS 变量注入 style="--frt-bg:#xxx" + class 内 var())
third_party_loginviews/oauth_bind.php:55style="padding:10px;" 静态样式§7.3 P1-3(应收敛为 class 或核心 mn-p- 工具类)

draft_saver 修复模板(对齐 §7.3 P1-6「i18n 文案注入 JS」的 data 属性承载方案):

// hook/layout_head_end.php
echo '
'; // assets/draft.js const cfg = document.getElementById('flintDraftCfg'); window.FlintDraftUserId = cfg ? Number(cfg.dataset.userId) : 0;

5.4 语言键缺失(1 处)

插件键位置说明
code_auditplugin.code_audit.truncatedviews/report.php:64三语字典(zh/en/zh_tw 各 43 键)均无此键 → 报告页在 truncated 为真时直接显示键名原文

修复:三个语言包同步补键(键序保持一致,UTF-8 无 BOM + LF):

'plugin.code_audit.truncated' => '结果已截断,仅显示前若干条',  // zh.php
'plugin.code_audit.truncated' => 'Results truncated; only the first entries are shown.',  // en.php
'plugin.code_audit.truncated' => '結果已截斷,僅顯示前若干筆',  // zh_tw.php

复核说明:扫描器另报 6 个插件共 6 条 i18n 缺键,逐键溯源后确认全部为动态拼接前缀误报(plugin.mod_system.cat.{类型}、plugin.task_center.seed.、plugin.user_level.lv{N}、plugin.ai_assistant.reply_type_、plugin.points_mall.type_、plugin.third_party_login.fld_ 等),对应键族在字典中均齐备。

5.5 结构与元数据(4 处)

插件位置问题规范条款
ai_assistantconfig.php.bak(6751 B,2026-08-18,早于 config.php 的 08-27)插件目录内残留备份文件§二「禁止在插件目录放 .bak/.revert/.tmp 等残留文件」
edit_infohook/post_edit_after.php(catch 内 CREATE TABLE IF NOT EXISTS edit_history)业务钩子内建表§11.3-16「建表放 activate() + init_after(惰性兜底)」
edit_infohook/thread_edit_after.php(同上)同上同上
post_favoriteplugin.json:13 "nav_pc_user_links": "hook/nav_pc_user_links.php"注册死钩子(核心 app/ 与 index.php 中已无任何触发点)§9.2「新插件不要再注册此钩子」

说明:

  • ai_assistant/config.php.bak 删除即可(config.php 为现行版本)。
  • edit_info 的两处属「表不存在则建表后重试」的惰性兜底,与 init_after 的定位相同,但按规范应把 DDL 上移到 activate()(或至少 init_after),业务钩子只做插入。
  • post_favorite 的钩子文件内容已改为 return;(空转),删除 plugin.json 中的注册项即可;注意改 hooks 清单后必须重建 plugins/plugins_cache.json(§11.5-30)。

六、P2 改进项(119 条)

类别数量涉及插件说明与建议
旧版兼容变量55ai_assistant(10)、icon_generator(18)、friend_links(7)、post_favorite(6)、user_profile(13)、content_review(1)使用 --text-dark / --primary-color / --border-color。这三个变量核心仍有定义(modern.css 各 9 处),属历史兼容;规范 §11.7 要求新代码一律 --mn-text / --mn-primary / --mn-border。建议逐步替换。
硬编码主色17icon_generator#0066cc 应改 var(--mn-primary)(assets/style.css:298 起 17 处)。
直查 users 表16medal(5)、mod_system(4)、content_review(2)、ai_assistant(1)、daily_checkin(1)、task_center(1)、user_level(1)、user_profile(1)§10.1 要求走 User::batchGetNames() / batchGetUsers()(请求级缓存,避免 N+1 与核心库压力)。
直读写 blogs/blog_comments7mod_system(5)、user_profile(2)实测两表仍活跃(blogs 16 行、blog_comments 5 行;退役的是 threads/posts)。mod_system 多数已包在 (new \app\Models\Blog())->queryOne(...) 内,仅 AdminController.php:301 / FrontController.php:239 直接走 Database::fetchOne;建议统一经 Model。
activate() 用 exec() 而非 ensureSchema()6ai_assistant、content_review、invite、medal、red_packet、user_level功能等价(Plugin::ensureSchema() 实现就是 self::db($name)->exec($ddl),app/Helpers/Plugin.php:86-89),仅风格不一致,属低优先。
视图内硬编码 URL5seo(3)、content_review(1)、post_favorite(1)§11.2-8 要求拼 BASE_PATH,否则二级目录部署 404。
IN (...) 用 intval + implode3content_review、medal、post_favorite安全(array_map('intval', ...) 已保证不可注入),但规范 §一五 红线 6 要求「intval 强转 + 生成占位符」。建议统一改 array_fill 占位符写法。
catch 内 rollBack() 无 inTransaction() 守卫3invite(1)、red_packet(2)§10.2 明确:若事务未开启,rollBack() 会抛 PDOException: There is no active transaction 并逃逸出 catch → 500(前端 res.json() 解析失败)。
命名空间风格3user_levelPlugin\user_level 非规范驼峰 Plugin\UserLevel。PHP 类名大小写不敏感,功能无影响(含 Plugin::uninstall() 的 class_exists("\Plugin\user_level\Plugin") 也能命中),纯风格问题。
内联数据块2seo、forum_required_tag<script type="application/json"> / ld+json。非可执行逻辑且已用 JSON_HEX_TAG 防闭合,属安全写法;规范未明确认可,建议改 data-* 属性承载(与 §7.3 P1-6 一致)。
语言键前缀1icon_generator70 个键用 plugin.icon_gen. 而非 plugin.icon_generator.。无功能影响(插件语言包按前缀合并,不与核心冲突),但违反 §八「键名统一 plugin.{插件名}.{语义} 前缀」。
未实现 activate()1xiuxianPlugin.php 有 ensureSchema() 与 ddl() 但无 activate() → 核心激活流程会因 method_exists 为假而跳过建表,实际依赖 init_after 兜底。建议补 activate()。


七、分插件总览

插件版本启用文件钩子前/后台permissionsP0P1P2
ai_assistant1.0.0✅495–/✅net:fetch, route:admin0412
announcements1.2.0✅163–/✅route:admin, system:settings040
code_audit1.0.0⛔292–/✅route:admin010
code_repo1.0.0⛔194–/✅route:admin, system:settings0170
content_review2.0.0⛔279✅/✅route:admin, system:settings036
daily_checkin1.0.0✅205–/–system:settings011
dice1.0.0⛔265–/–(未声明)000
draft_saver1.0.0✅61–/–[]010
edit_info1.5.0✅134–/–(未声明)020
floor_reply1.1.0✅216✅/✅route:admin, system:settings000
forum_required_tag1.1.0✅237✅/✅route:admin051
friend_links2.0.0✅194✅/✅route:admin, system:settings057
icon_generator1.0.0⛔112✅/–(未声明)02536
invite1.5.0⛔166–/–(未声明)112
medal1.0.0✅249–/–route:admin057
mod_system1.0.0✅3011✅/✅route:admin, system:settings039
points_mall1.0.0✅227✅/✅route:admin010
post_favorite1.0.0✅185–/–(未声明)038
qa1.0.0⛔298–/✅route:admin, system:settings020
quiz_duel1.0.0⛔214–/–(未声明)020
red_packet1.8.0✅206–/–(未声明)0293
seo1.5.0✅185✅/✅route:admin, system:settings044
single_page1.0.0✅194✅/✅route:admin000
task_center1.0.0✅2611✅/✅route:admin001
third_party_login1.0.0⛔225✅/✅route:admin, net:fetch010
user_level1.0.0✅2811✅/✅route:admin, system:settings105
user_profile1.0.0✅164✅/–route:admin0116
xiuxian1.0.0✅244✅/–[]011
合计3902121119

零问题插件:dice、floor_reply、single_page(3 个)。


八、通过项(合规确认)

以下维度经全量扫描 + 抽样复核,未发现任何违规:

8.1 安全(8 条最高级别红线)

检查项结果
反序列化 / 动态执行(unserialize/eval/assert/create_function/preg_replace /e)✅ 0 处
$_SERVER['DOCUMENT_ROOT'] 绝对路径✅ 0 处
渲染钩子内 die()/exit()/header()✅ 0 处(唯一命中在 seo/hook/layout_head_end.php:6 的注释中,已排除)
上传弱随机名 / 缺白名单(move_uploaded_file)✅ 0 处
IN (...) 拼接用户输入(真实注入面)✅ 0 处(命中项经溯源均为占位符 / intval / quote() / 白名单守卫)
覆盖 Plugin::db() 的 PDO 属性✅ 0 处
AJAX / 表单 CSRF✅ 0 缺失 —— 逐方法扫描全部含 $_POST 且含写操作的控制器方法,100% 调用 Csrf::verify/verifyOrDie
路径穿越(文件函数直用 $_GET/$_POST/$_FILES)✅ 0 处
静态调用核心 Model 实例方法✅ 0 处(ai_assistant/cli/diag_thread.php 已正确用 (new \app\Models\Thread())->find())
后台鉴权✅ 17 个插件后台控制器全部 use app\Controllers\Admin\BaseController(统一走 Auth::isAdmin() + 二次验证门禁)
开放重定向✅ 0 处(user_level/hook/route_before_dispatch.php 的 ulGateBackUrl() 做了同源 + BASE_PATH 校验)

8.2 结构、元数据与钩子

检查项结果
PHP 语法✅ 390 个文件,0 错误(token_get_all + TOKEN_PARSE)
plugin.json✅ 28/28 存在、合法 JSON、无 BOM;name/version/author/description/activated/hooks 必填字段齐备
hooks 路径✅ 全部使用 / 分隔;指向文件全部存在;无未知钩子名
plugins_cache.json 一致性✅ 28 个插件的 hooks 清单与 plugin.json 零差异
死钩子⚠️ 仅 post_favorite 1 处(见 P1)
双触发钩子双 include 回归✅ 23 个文件全部输出一致(23/23),0 处 Cannot redeclare
钩子顶层符号声明✅ 仅 user_level/hook/route_before_dispatch.php:46 1 处,且是规范 §9.4 的正例参考(function_exists 包裹 + 置于文件顶部)
admin_sidebar_links 注册✅ 0 处(符合「新插件一律不注册,改用 admin_url」)
导航钩子图标字号✅ nav_plugin_links 全部 16px(13 处);nav_user_menu_items 全部 14px(5 处)
nav_plugin_links 高亮判定✅ 11/13 正确用 __nav_uri;2 处偏差(qa、xiuxian 读 $_SERVER['REQUEST_URI'])——规范 §9.3 已记录该偏差,二级目录部署下易失效,建议后续统一

8.3 数据库与 i18n

检查项结果
插件独立库(Plugin::db())✅ 全部使用独立库,无写全局 business.sqlite
SQLite DDL 语法✅ 0 处 MySQL 写法(AUTO_INCREMENT/VARCHAR(n)/ENGINE=InnoDB/SHOW TABLES LIKE 等;ai_assistant/cli/ 中的 datetime('now','localtime') 是合法 SQLite 函数)
读写退役表 threads/posts✅ 0 处(均已迁移至 main_index.topic_index)
三语语言包✅ 26 个有 lang/ 的插件,zh/en/zh_tw 键数完全相同、键集合零差异
语言包编码✅ 无 BOM
引用键 vs 字典全量比对⚠️ 仅 1 处真实缺失(code_audit.truncated,见 P1)


九、修复优先级建议

第 1 批 —— 立即(P0,2 条)

  1. plugins/invite/InviteController.php:46 → beginTransaction();:59 catch 内补 inTransaction() 守卫。
  2. plugins/user_level/views/admin.php:273 → 内联 onclick 改 data-ul-confirm + assets/script.js。

第 2 批 —— 本迭代(P1 核心库直写 6 条)

  1. daily_checkin / quiz_duel / red_packet 的 5 处积分直写改 Points::award() / Points::deduct();daily_checkin 同时补齐 points_log。
  2. points_mall/Plugin.php:508 改 User::updateGroupId()。

⚠️ 这几处涉及积分与用户组,改动前务必按 §11.8-1 核对 Points 接口(award/deduct 均带条件原子更新与 rowCount() 检查),并在改动后回归「积分扣减无负数、等级联动、points_deduct_after 钩子触发」三项断言。

第 3 批 —— 本迭代(P1 其余 115 条)

  1. 主题硬编码 105 处:按 §11.7 对照表批量替换为 var(--mn-*, fallback),改完必须双主题(浅色 + 暗夜星辰)走查。
  2. 零内联 5 处(draft_saver 1、forum_required_tag 3、third_party_login 1)。
  3. code_audit 补 plugin.code_audit.truncated 三语键(注意键序一致、UTF-8 无 BOM + LF)。
  4. 删 ai_assistant/config.php.bak;edit_info 两处 DDL 上移;post_favorite 移除死钩子注册并重建 plugins_cache.json。

第 4 批 —— 排期(P2 119 条)

  1. 旧变量 55 处 → --mn-*;icon_generator 主色 17 处 → --mn-primary。
  2. 直查 users 表 16 处 → User::batchGetNames()/batchGetUsers()。
  3. 其余风格项(ensureSchema、占位符、inTransaction 守卫、命名空间、xiuxian::activate())随相关文件改动时顺带修正。

发布前必做(§11.5 / §11.7)

# ① 改过 hooks 清单的插件 → 重建缓存
#    删除 plugins/plugins_cache.json 或调用 \app\Helpers\Plugin::refresh()
# ② 改过 nav_* / layout_head_end / footer_links → 清前台页面缓存
#    \app\Helpers\PageCache::invalidate()   (注:只覆盖列表页且仅游客)
# ③ 零内联复查(应零命中,


十、附录

10.1 审计产物

文件说明
.workbuddy-ai/tmp/audit2.py静态扫描器(词法预处理 + 9 大维度规则)
.workbuddy-ai/tmp/audit2.json扫描原始结构化结果(含全部命中项与文件行号)
.workbuddy-ai/tmp/lint_plugins.phpPHP 语法校验(单进程)
.workbuddy-ai/tmp/reinclude_test.php双触发钩子双 include 回归
.workbuddy-ai/tmp/confirmed.txt剔除误报后的确认清单

10.2 扫描器误报成因(供后续复用参考)

误报类型成因修正方式
注释内关键词正则未剥离 //、/ /引入 PHP 词法预处理,注释与字符串分别置空
SQL 占位符IN ({$ph}) 中 $ph = implode(',', array_fill(0, n, '?'))对 SQL 内插变量做赋值溯源:含 array_fill+? → 安全
已转义拼接->quote($x)、array_map('intval', ...)识别 quote/intval 即降级
白名单守卫if (!in_array($table, [...], true)) return; 后再内插人工复核确认(正则难以自动判定)
合法 fallbackvar(--mn-bg-card, #fff) 中的 #fff 是规范要求的浅色兜底值排除 var(--mn-*, #hex) 同行命中
变量名子串$datetime 命中 DATETIME(MySQL 类型检测)加 (?<![A-Za-z_$]) 前置否定 + (?!\s*\() 排除函数调用
动态 i18n 键$this->t('plugin.x.lv' . $n) → 提取出 plugin.x.lv逐键溯源到字典中的键族

10.3 已知但未计入的问题(规范 §11 已登记,本次未重复发现)

  • mod_system 的 users.status 不校验、写路径零事务;
  • AuditLog 无痕、外部正文锁不一致;
  • pagination() 出口不转义、搜索页 type 反射 XSS;
  • fr_replies.thread_id 有 1 处既有不一致(id=28 post=1425 存 76,实际主题 81)—— 已记录未改。

以上均属核心层问题,不属于插件规范范畴,故未计入本报告问题总数。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 1 次 | 回复 0 次

全部回复 (0)

暂无回复

请 登录