fix: 固化NapCat验证码日志审查

This commit is contained in:
sunlei 2026-06-13 12:03:01 +08:00
parent fbcf46516e
commit 640c2dbf7e
2 changed files with 26 additions and 12 deletions

View File

@ -23,7 +23,7 @@
- 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。 - 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。
- 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮模板目录永久保留CLI 默认 dry-run真实清理必须显式传 `--execute` - 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮模板目录永久保留CLI 默认 dry-run真实清理必须显式传 `--execute`
- 检查 env 策略和变更风险:区分后端真实 env 与前端客户端 `.env*`提醒锁文件、核心表格组件、API 时间序列化 KtDateTime 列/DTO 装饰器入口、部署链路和 Vue TSX 插槽写法等高风险改动。 - 检查 env 策略和变更风险:区分后端真实 env 与前端客户端 `.env*`提醒锁文件、核心表格组件、API 时间序列化 KtDateTime 列/DTO 装饰器入口、部署链路和 Vue TSX 插槽写法等高风险改动。
- 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、NapCat `latest` 镜像漂移风险、QQBot 重型服务单测导入风险、QQBot OneBot/QQ 登录态边界回归、NapCat 密码登录验证码清理前 pending 回归、根目录生成产物、`TASKS.md` 最近记录字段结构和当前变更风险;默认只对变更文件做内容扫描,并放过短中文显示标签和明显测试占位 token避免历史误报污染上下文任何文件改动后都要跑一遍。 - 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、NapCat `latest` 镜像漂移风险、QQBot 重型服务单测导入风险、QQBot OneBot/QQ 登录态边界回归、NapCat 密码登录验证码 wait 阶段和清理前 pending 回归、普通处理中状态误吃旧验证码日志回归、根目录生成产物、`TASKS.md` 最近记录字段结构和当前变更风险;默认只对变更文件做内容扫描,并放过短中文显示标签和明显测试占位 token避免历史误报污染上下文任何文件改动后都要跑一遍。
- 生成或写入 `TASKS.md` 最近记录:默认 `dryRun=true`,确认后再落盘。 - 生成或写入 `TASKS.md` 最近记录:默认 `dryRun=true`,确认后再落盘。
- 生成提交前检查清单:校验 KT commit message 约定。 - 生成提交前检查清单:校验 KT commit message 约定。
@ -106,7 +106,7 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
| 脚本 | 用途 | | 脚本 | 用途 |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pnpm run admin-login` | 使用可见 Edge 打开 Admin 登录页,填写账号密码,拖动滑块,保存登录态和截图。默认账号来自初始化数据 `admin/123456`,生产或个人账号用 `KT_ADMIN_USERNAME` / `KT_ADMIN_PASSWORD` 或 CLI 参数覆盖。 | | `pnpm run admin-login` | 使用可见 Edge 打开 Admin 登录页,填写账号密码,拖动滑块,保存登录态和截图。默认账号来自初始化数据 `admin/123456`,生产或个人账号用 `KT_ADMIN_USERNAME` / `KT_ADMIN_PASSWORD` 或 CLI 参数覆盖。 |
| `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告,并校验 `TASKS.md` 最近记录只保留范围、关键词、验证字段、NapCat `latest` 漂移、QQBot 重型服务单测导入风险、OneBot/QQ 登录态边界和 NapCat 密码登录验证码清理前 pending 回归;明显测试占位 token 不报凭据风险,确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 | | `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告,并校验 `TASKS.md` 最近记录只保留范围、关键词、验证字段、NapCat `latest` 漂移、QQBot 重型服务单测导入风险、OneBot/QQ 登录态边界、NapCat 密码登录验证码 wait 阶段及清理前 pending 回归,以及普通处理中状态误吃旧验证码日志回归;明显测试占位 token 不报凭据风险,确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 |
| `pnpm run obsidian-context` | 输出 Obsidian 索引上下文;可传 `--module Admin`、`--query QQBot`、`--max-documents 10`。 | | `pnpm run obsidian-context` | 输出 Obsidian 索引上下文;可传 `--module Admin`、`--query QQBot`、`--max-documents 10`。 |
| `pnpm run obsidian-validate` | 校验 KT Obsidian vault 结构和链接;默认 warning 不让脚本失败,需要严格模式时传 `--fail-on-warnings`。 | | `pnpm run obsidian-validate` | 校验 KT Obsidian vault 结构和链接;默认 warning 不让脚本失败,需要严格模式时传 `--fail-on-warnings`。 |
| `pnpm run obsidian-sync` | 审计 Obsidian 工作流入口是否连通,并联动执行 validate。 | | `pnpm run obsidian-sync` | 审计 Obsidian 工作流入口是否连通,并联动执行 validate。 |

View File

@ -346,15 +346,21 @@ export function findQqbotNapcatCaptchaFlowFindings(
const content = lines.join("\n"); const content = lines.join("\n");
const findings: ReviewFinding[] = []; const findings: ReviewFinding[] = [];
const waitLine = lines.findIndex((line) => const waitLine = lines.findIndex((line) =>
/waitForPasswordLoginStatus\s*\(/.test(line), /private\s+async\s+waitForPasswordLoginStatus\s*\(/.test(line),
); );
const resolveLine = lines.findIndex((line) => const resolveLine = lines.findIndex((line) =>
/resolvePasswordCaptchaUrl\s*\(/.test(line), /private\s+async\s+resolvePasswordCaptchaUrl\s*\(/.test(line),
); );
const helperLine = lines.findIndex((line) =>
/private\s+async\s+detectPasswordCaptchaUrl\s*\(/.test(line),
);
const waitBody =
waitLine >= 0 ? lines.slice(waitLine, waitLine + 90).join("\n") : "";
if ( if (
content.includes("waitForPasswordLoginStatus") && content.includes("waitForPasswordLoginStatus") &&
!content.includes("isNapcatCaptchaRequiredMessage(errorMessage)") (!waitBody.includes("isNapcatCaptchaRequiredMessage(errorMessage)") ||
!waitBody.includes("detectPasswordCaptchaUrl("))
) { ) {
findings.push({ findings.push({
category: "qqbot-napcat-captcha-error-throws-before-pending", category: "qqbot-napcat-captcha-error-throws-before-pending",
@ -370,16 +376,24 @@ export function findQqbotNapcatCaptchaFlowFindings(
} }
const resolveBody = const resolveBody =
resolveLine >= 0 ? lines.slice(resolveLine, resolveLine + 35).join("\n") : ""; resolveLine >= 0 ? lines.slice(resolveLine, resolveLine + 60).join("\n") : "";
const helperBody =
helperLine >= 0 ? lines.slice(helperLine, helperLine + 30).join("\n") : "";
const runtimeLogReads = ( const runtimeLogReads = (
resolveBody.match(/detectRuntimeCaptchaUrl\s*\(/g) || [] helperBody.match(/detectRuntimeCaptchaUrl\s*\(/g) || []
).length; ).length;
const boundedRuntimeLogRead = /detectPasswordCaptchaUrl\s*\(\s*container\s*,\s*sinceMs\s*,\s*false\s*,?\s*\)/.test(
resolveBody,
);
const captchaTailFallbackRead = /detectPasswordCaptchaUrl\s*\(\s*container\s*,\s*sinceMs\s*,\s*true\s*,?\s*\)/.test(
resolveBody,
);
if ( if (
resolveBody && resolveBody &&
(runtimeLogReads < 2 || (!resolveBody.includes("detectPasswordCaptchaUrl(") ||
!resolveBody.includes( runtimeLogReads < 2 ||
"isNapcatCaptchaRequiredMessage(loginStatus.loginError)", !boundedRuntimeLogRead ||
)) !captchaTailFallbackRead)
) { ) {
findings.push({ findings.push({
category: "qqbot-napcat-captcha-log-single-read", category: "qqbot-napcat-captcha-log-single-read",
@ -390,7 +404,7 @@ export function findQqbotNapcatCaptchaFlowFindings(
"验证码 URL 日志只读一次,时间窗偏差时可能在清理前拿不到 proofWaterUrl。", "验证码 URL 日志只读一次,时间窗偏差时可能在清理前拿不到 proofWaterUrl。",
project: context.project, project: context.project,
suggestion: suggestion:
"先按本次重启时间窗读取日志,确认是验证码场景但未取到 URL 时,再读取当前容器 tail 兜底。", "resolve 阶段调用统一日志提取 helperhelper 先按本次重启时间窗读取日志,再读取当前容器 tail 兜底。",
}); });
} }