🍲 sip 全面测试报告
测试日期:2026-08-11 | 测试对象:
sip-win-x64.exe(sip RSS 阅读器 v. 单文件版) 测试方式:CLI 全命令覆盖 + 源码审查 + 故障注入 + 压力测试 + 安全渗透
一、测试概述
1.1 测试环境
| 项目 | 说明 |
|---|---|
| 程序 | E:\test\sip-win-x64.exe(.NET 10 单文件发布) |
| 数据目录 | E:\test\readwithhotsoup\(SQLite + 全文缓存 + 语言文件) |
| AI 配置 | Embedding:baai/bge-m3 @ https://open.cherryin.net/v1;LLM:deepseek-v4-flash |
| 订阅源 | 热汤茶馆(15) / Hugging Face Blog(838) / CGTN World(49) / 瓦解的生活记事(20),共 922 篇 |
| 测试手段 | 51 项功能测试 + 30+ 项边界/异常注入 + 安全渗透 + 数据量压测 + 并发测试 |
1.2 测试方法
- 全部走 CLI(
--ignoresafeannouncement+--json结构化输出),与 AI Agent 真实使用路径一致 - 边界/异常测试构造了:恶意 RSS、内网回环服务器、挂起服务器、实体炸弹、损坏数据库、1 万篇巨型源等真实攻击载荷
- 关键发现均以源码行号佐证
二、测试统计总览
| 类别 | 测试数 | 通过 | 发现缺陷 |
|---|---|---|---|
| 核心功能 | 21 | 20 | 1 |
| 边界/异常 | 16 | 13 | 3 |
| 安全渗透 | 10 | 6 | 4 |
| 压力测试 | 12 | 9 | 3 |
| 合计 | 59 | 48 | 11 |
缺陷分布:严重 4 | 中等 5 | 轻微 2
三、核心功能测试结果
3.1 订阅管理 ✅
| 测试项 | 命令 | 结果 |
|---|---|---|
| 列出订阅源 | -l | ✅ 4 源正常,含健康状态 |
| 添加订阅源 | -d <url> | ✅ 4 源全部入库(HF 838 篇 / CGTN 49 篇 / hin.cool 20 篇) |
| 重复源去重 | 重复 -d 已存在源 | ✅ 识别「已存在」,跳过不重复入库 |
| 更新源 | -u <编号> | ✅ 新旧比对正确,无变化时「跳过更新」 |
| 文章列表 | -l <编号> | ✅ 双格式编号 [序号/真实ID] 正确 |
| 删除源(含数据清理) | -r <编号> --yes | ✅ Items/向量/全文缓存全部清理干净 |
| 归档/去归档 | -a / -una | ✅ 标题时间戳往返正确 |
3.2 检索能力 ✅
| 测试项 | 结果 |
|---|---|
全文搜索 --grep(不依赖 AI) | ✅ 精确命中,输出「编号+标题+出现次数+片段」 |
语义搜索 --search(bge-m3) | ✅ 中文/英文查询均命中,相似度 0.5-0.7 |
| 搜索相关性 | ✅ 「NVIDIA voice agent latency」准确命中 Magpie TTS 一文(0.663) |
读全文 --show <id> --json | ⚠️ 只输出数据库 Content,不合并全文缓存(见问题 #6) |
全文抓取 --fulltext | ✅ 抓取成功、文件缓存、重复抓取走缓存 |
3.3 招牌功能:版本追踪 / Diff ✅
| 测试项 | 结果 |
|---|---|
版本标记 ✎ | ✅ 有历史版本的文章正确显示 |
--versions | ✅ v2 现行 + v1 已归档,带时间戳 |
--diff | ✅ 结构化变更输出({type, before, after}) |
| 旧版读取 | ✅ --show <旧版ID> --json 可读任意历史版本 |
3.4 AI 能力
| 测试项 | 结果 |
|---|---|
摘要生成 --summary | ✅ DeepSeek 调用成功,中文摘要质量良好 |
| 摘要缓存复用 | ✅ 首次 2668ms → 二次 499ms,只调一次 LLM |
| 语义搜索-标题向量 | ✅ 命中良好 |
| 语义搜索-全文向量(sidecar) | ⚠️ 存在阈值陷阱(见问题 #8) |
--init 非 TTY | ❌ 直接崩溃(见问题 #5) |
3.5 其他功能 ✅
| 测试项 | 结果 |
|---|---|
| OPML 导出→导入→再导出 | ✅ 幂等,导入正确跳过已存在源 |
| 调度设置与到期计算 | ✅ 「30 分钟 · 上次 X 前 · 下次 Y 后」计算正确 |
--sync 到期更新 | ✅ 只更新到期源,无到期时正确空转 |
--today 今日清单 | ✅ 规则式选文 5 篇,含理由与时长 |
语言切换 --lang en-US | ✅ 界面完整切换 |
--like / --likes | ✅ 用户点赞 + AI 标记(🤖)正常 |
| telemetry 全流程 | ✅ enable/status/show/export 正常,0 事件时优雅处理 |
四、缺陷清单(按严重度分级)
🔴 严重(High)
| # | 缺陷 | 复现路径 | 根因(源码) | 影响 |
|---|---|---|---|---|
| 1 | -l <源> 列表 O(n²) 性能地雷 | 单源 2000+ 篇后列表操作卡死:1000 篇 1.2s → 2000 篇 16.9s → 5000 篇 >30s → 10000 篇 >60s | ListArticlesFromDb:对每篇文章执行 3 个 Guid 相关子查询(COUNT/MAX),无索引全表扫描 | 任何单源超过 ~1500 篇即不可用;真实大型博客源即可触发 |
| 2 | 主数据库损坏无容错 | 写坏 rss.db 后任意命令 → Unhandled exception: database disk image is malformed 崩溃 | rss.db 打开无完整性检查(telemetry.db 有完整自愈机制,主库没有) | 数据损坏时用户无任何提示,直接崩溃 |
| 3 | SSRF:恶意 RSS 可探测/抓取内网 | 构造 link 指向 http://127.0.0.1:18999/secret 的恶意 RSS → --fulltext → 服务器日志证实请求发出,内网数据被回捞缓存 | FetchAndExtract 对文章 link 无协议/内网白名单校验 | 可探测本机服务、内网主机、云元数据(169.254.169.254) |
| 4 | 终端注入:恶意内容控制终端 | 文章注入 \x1b ESC 字节 → --grep 输出透传:^[[2J(清屏)、^[]0;(改标题)、^[[31m(变色) | EscapeMd 只转义 \ * # [ ] |,不处理 ESC 控制字符 | 交互终端可被伪装/清屏钓鱼;AI/管道场景免疫 |
🟡 中等(Medium)
| # | 缺陷 | 说明 |
|---|---|---|
| 5 | --init 在非 TTY 崩溃 | ReadSecret→Console.ReadKey 无控制台直接抛 InvalidOperationException。API Key 只能人工终端填写,AI Agent 无法完成 AI 配置——与「AI 友好」定位冲突 |
| 6 | --show <id> --json 不合并全文缓存 | AI 读全文的主路径只输出数据库 Content;HF 这类 RSS 无正文源,AI 拿到空 content,必须先 --fulltext 再 --export/TUI 读。SKILL.md 表述有误导 |
| 7 | 退出码契约不一致 | --show 0、--diff 999999、--export 99999、--summary 99999、--frobnicate(未知命令)报错但退出码全为 0;仅 --like 99999 正确返回 3。README「结构化退出码」承诺部分失效 |
| 8 | 全文向量阈值陷阱 | sidecar 全文向量命中分比标题向量低 0.1-0.2(实测 0.44 vs 0.66);默认阈值 0.5 按标题向量调,导致「正文独有概念」搜不到。文档阈值表未区分两种向量 |
| 9 | 全文向量生成时序缺陷 | 先 --fulltext 后 --index 的源:已抓全文因「源未索引」跳过 sidecar 生成;之后重跑 --fulltext 走缓存短路永不补齐,只能 --purge-fulltext 重抓 |
🟢 轻微(Low)
| # | 缺陷 | 说明 |
|---|---|---|
| 10 | --grep 通配符未转义 | --grep "%" / --grep "_" 匹配全部文章却报「出现 0 次」(SQL LIKE 通配符无转义) |
| 11 | --export-opml 坏路径崩溃 | DirectoryNotFoundException 未处理异常直接抛出(全程序唯一裸崩路径之一) |
| — | ai_config.json 端点缺协议头 | 配置 open.cherryin.net/v1 会令搜索崩溃(用户手工修复后正常);程序应容错补全 |
| — | 同意短语管道输入编码不匹配 | 中文同意短语经管道输入(GBK 控制台)比对失败,Agent 必须用 --yes |
五、安全评估
5.1 防护良好 ✅
| 攻击面 | 测试载荷 | 结果 |
|---|---|---|
| SQL 注入 | ' OR 1=1 -- / '; DROP TABLE Items;-- | ✅ 参数化查询,全部无效 |
| XML 实体炸弹 | Billion Laughs(9 层嵌套 → 10⁹ 字符) | ✅ MaxCharactersFromEntities 拦截 |
| XXE 外部实体 | file:///C:/Windows/win.ini | ✅ 解析为空串,本地文件不可读 |
| 畸形 XML | 二进制垃圾 / 空文件 / 5MB 未闭合标签 | ✅ 三种均优雅报错,零崩溃 |
| API Key 存储 | 源码审查 | ✅ Windows 凭据管理器,不落盘 |
| telemetry 隐私 | 源码审计全部网络调用点 | ✅ 无任何上传逻辑,纯本地 |
| 本地文件读取 | -d file:///C:/Windows/win.ini | ✅ 意外被「补协议」逻辑挡掉 |
5.2 实测漏洞 ⚠️
| 漏洞 | 严重度 | 实测证据 |
|---|---|---|
| SSRF(全文抓取无协议/内网校验) | 🔴 高 | 恶意 RSS link → 内网 /secret 被请求,TOP SECRET INTERNAL DATA 被抓回缓存 |
| 终端注入(ESC 控制字符透传) | 🔴 高 | --grep 输出含 ^[[2J/^[]0; 真实 ESC 字节 |
javascript: 链接注入 | 🟡 中 | <script>/onerror 被剥离,但 [点我](javascript:alert%281%29) 保留;TUI 链接导航经 Process.Start 直接打开,无协议白名单 |
六、压力测试专项
6.1 性能基线(922 篇常规规模)
| 操作 | 耗时 |
|---|---|
-l 列表 | 0.48s |
--grep 全文搜索 | 0.49s |
| 1 万篇 RSS 下载解析 | 5.8s |
6.2 O(n²) 曲线(-l <单源> 耗时 vs 文章数)
500 篇 0.8s ← 正常
1000 篇 1.2s ← 临界点附近
2000 篇 16.9s ← 明显恶化
5000 篇 >30s ← 超时
10000 篇 >60s ← 卡死6.3 并发与韧性
| 测试 | 结果 |
|---|---|
| 5 进程并发读 | ✅ 全部成功,库完整 |
| 4 源并发写更新 | ✅ 全部成功,integrity ok |
| 挂起服务器(120s 不响应) | ✅ 25.6s 精确超时 + FETCH_FAILED |
| telemetry.db 并发损坏 | ✅ 自动重建,rss.db 全程无损 |
| 语言文件删除 | ✅ 内置副本自动恢复,不覆盖自定义 |
| 10 万字符巨型标题 | ✅ 全量渲染无截断(grep 输出 100KB) |
七、评分
7.1 维度评分(10 分制)
| 维度 | 权重 | 得分 | 依据 |
|---|---|---|---|
| 功能完整性 | 25% | 8.5 | 全功能 CLI + JSON 输出 + 退出码设计;版本追踪/Diff/OPML/调度等招牌功能扎实;仅个别规划项未实现 |
| 稳定性/鲁棒性 | 20% | 6.0 | 大多数边界处理优雅(超时/畸形 XML/并发/自愈),但主库损坏裸崩、-l O(n²) 卡死、export-opml 裸崩扣分严重 |
| 性能 | 15% | 7.0 | 常规规模极快(<0.5s),下载解析高效;O(n²) 地雷 + 跨源 search 全量扫描是硬伤 |
| 安全性 | 20% | 6.5 | SQL 注入/XXE/实体炸弹/隐私全防(基础扎实);但 SSRF + 终端注入 + javascript: 链接三个真实漏洞未封堵 |
| 文档质量 | 10% | 7.5 | SKILL.md 质量高、结构清晰;但未覆盖实测发现的坑(时序、阈值、非 TTY、通配符) |
| AI/Agent 友好 | 10% | 6.5 | JSON + 退出码设计理念好;但 --init 非 TTY 崩溃、退出码契约不一致、--show 不合并全文,对 Agent 是真实障碍 |
7.2 总分
8.5×0.25 + 6.0×0.20 + 7.0×0.15 + 6.5×0.20 + 7.5×0.10 + 6.5×0.10
= 2.13 + 1.20 + 1.05 + 1.30 + 0.75 + 0.65
= 7.08 / 107.3 综合评级
| 评级 | 说明 |
|---|---|
| 总分:7.1 / 10(B+ / 良好) | 一款功能扎实、设计用心(数据本地、版本即事实、AI 白名单理念出色)的 RSS 阅读器。作为个人日常工具完成度高;但作为「给 AI 代理当数据源」的定位,鲁棒性和安全边界还有明显欠账,不建议在大型源上无脑全量索引,也不建议在不可信网络下对任意 RSS 源执行全文抓取 |
八、修复优先级建议
| 优先级 | 项目 | 工作量 | 方案 |
|---|---|---|---|
| P0 | -l N O(n²) | 小 | 给 Items.Guid 加索引 + 子查询改 JOIN/窗口函数 |
| P0 | 终端注入 | 一行为单位 | EscapeMd 追加 \x1b(及 \a)过滤 |
| P1 | 主库损坏容错 | 小 | 启动/打开时仿 telemetry 做完整性检查 + 保留现场提示 |
| P1 | SSRF | 中 | FetchAndExtract 增加 http/https 协议白名单 + 可选内网地址拦截 |
| P1 | --init 非 TTY | 小 | ReadSecret 捕获无控制台异常降级为 ReadLine |
| P1 | 退出码契约 | 小 | 统一所有报错路径的 SetExit(code) 语义 |
| P2 | --show --json 合并全文 | 小 | JSON 输出增加 fulltext 字段(有缓存时) |
| P2 | --grep 通配符转义 | 一行为单位 | SQL LIKE 参数转义 %/_ |
| P2 | javascript: 协议白名单 | 小 | Process.Start 前校验 http/https |
九、附录
9.1 测试中值得记录的正面设计
- 「全文抓取同意短语」——抓源站前强制用户明确同意,尊重版权,业界少见
- telemetry 自愈——损坏自动重建且绝不碰主库,README 承诺实测兑现
- 版本即事实——Guid 分组 + diff,信息演变全程可追溯(Guid 实为文章 URL)
- 摘要缓存复用——不重复烧 token,实测验证
- 畸形输入处理——二进制/空/超长/实体炸弹全部优雅拒绝,超出大多数同类工具
9.2 测试遗留说明
- 测试产生的全部数据已清理(压测源/恶意源/临时文件),数据库恢复 922 篇 / integrity ok
- 遗留:feed 1 调度已恢复
manual;--like标记保留 1 条 AI 标记记录 - 测试过程中「热汤茶馆」因真实 RSS 更新新增 1 篇(已并入 922 篇统计后恢复)
报告生成:2026-08-11 | 全部结论基于实测复现 + 源码行号佐证