[C-07][low] 消息校验与小问题合集:ttl 可为 0 或负数、delay 溢出变立即发送、提交不检查发送方已停用、群分发不按入群时间过滤、保留期按创建时间算 #38

Closed
opened 2026-09-30 13:57:00 +08:00 by nixevol · 1 comment
Owner

编号:C-07 严重级:low 工作线:消息核心(internal/app/message、serve 的 messageLoops) 来源:审查 M-16、M-18、I-16、M-19
依赖:第 4 点依赖 C-03 (#34)、C-04 (#35) 被依赖:无

结论与统一方案

  1. keep 时 ttl_seconds<=0 返回 bad_request;先判断 *DelayMs > MaxScheduleSeconds*1000 再做加法;Send.Validate 同步补上。(审查 M-16)
  2. 写事务内检查发送方 enabled,已停用就拒绝,错误码建议 unauthorized 并记入 DEVIATIONS。(审查 M-18)
  3. 群分发的接收者查询加 AND gm.joined_at <= ?(参数为 send_at),PRD F06"发送时刻之后才入群的端收不到"。(审查 I-16)
  4. 新增 completed_at 列,所有完成路径(配合 C-04 的统一函数)写入,清理按 completed_at 判断,并在 DEVELOPMENT 7.6 写明口径。迁移号排在 C-03 的 0003 之后,或与 C-03 合并。(审查 M-19)

改动文件

internal/app/message/submit.go、dispatch.go、recover.go;internal/protocol/validate.go;迁移文件。

与其他问题的交互 / 冲突说明

submit.go 与 C-05、U-03 改不同位置;第 4 点依赖 C-03、C-04。

验收与测试

  • 表驱动覆盖 ttl=0、ttl=-1、delay=MaxInt64。
  • 停用后再提交被拒绝,且没有产生任何投递。
  • 发送时刻为 T 的消息,成员在 T+500ms 入群,分发后该成员没有投递。
  • 30 天前创建、今天才完成的消息,清理后记录仍在。

问题明细(各区审查原文,证据含文件与行号)

以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。

[M-16] 发送参数边界校验缺失:ttl_seconds 可以为 0 或负数,delay_ms 溢出后变成立即发送

  • 严重级:low
  • 分类:协议一致性
  • 现象与影响:
    • keep:true 配 ttl_seconds<=0 会被接受,expire_at 落在过去,结果取决于推送和清理谁先执行。
    • 超大的 delay_ms 使 nowMs+delay 溢出成负数,绕过 max_schedule_seconds 检查,变成立即发送。
  • 证据:submit.go:52-54(只查上限)、submit.go:301;protocol/validate.go:57-65。
  • 文档依据:DEVELOPMENT 6.2 保留时长上限和定时上限;PRD F09、F11。
  • 为何不是故意设计:DEVIATIONS 里没有相关说明。
  • 解决方案:ttl<=0 返回 bad_request;先判断 *DelayMs > MaxScheduleSeconds*1000 再做加法;协议包的 Send.Validate 同步补上。
  • 改动文件:internal/app/message/submit.go(以及协议包,属共享文件)。
  • 交互/冲突风险:SDK 本地如有同类校验要保持一致。
  • 需补测试:表驱动覆盖 ttl=0、ttl=-1、delay=MaxInt64。
  • 置信度:代码阅读确定。

[M-18] 提交时不检查发送方是否已停用

  • 严重级:low
  • 分类:逻辑
  • 现象与影响:Submit 在事务内外都没有检查发送方的 enabled。停用写库完成到连接真正断开之间(发 fatal 后约 20 毫秒),上行队列里已经排着的 send 仍会被接受并投递;如果停用时恰好碰上重连(踢线没找到连接),窗口会更长。
  • 证据:submit.go:65-71、submit.go:203-211。
  • 文档依据:PRD F01「停用:立刻断开,不能再登录……它自己发出、还没推送出去的消息也作废」。
  • 为何不是故意设计:DEVIATIONS 里没有相关说明。
  • 解决方案:在写事务内检查 snd.Enabled,已停用就拒绝。错误码建议用 unauthorized,并记入 DEVIATIONS。
  • 改动文件:internal/app/message/submit.go。
  • 交互/冲突风险:与 identity 停用流程无冲突。
  • 需补测试:停用后再提交被拒绝,且没有产生任何投递。
  • 置信度:代码阅读确定。

[I-16] 群消息分发不按入群时间过滤(跨模块)

  • 严重级:low
  • 分类:与PRD不符
  • 现象与影响:分发时取的是分发那一刻的成员,没有 joined_at <= send_at 条件。分发由每秒一次的循环执行,每轮最多 100 条。在发送时刻和实际分发之间入群的人(通常不到 1 秒,积压时可达分钟级)会收到入群前发出的消息。
  • 证据:message/dispatch.go:90-94;cmd/nixmsg/serve.go:322-333。
  • 文档依据:PRD F06(210-211)「发送时刻之后才入群的端收不到」。
  • 为何不是故意设计:没有偏差记录,joined_at 字段本来就有。
  • 解决方案:接收者查询加上 AND gm.joined_at <= ?,参数为 send_at。
  • 改动文件:message/dispatch.go(消息线)。
  • 与其他模块的交互/冲突风险:退群后重新入群的人 joined_at 会更新,自然被排除,符合 PRD。
  • 需补测试:发送时刻为 T 的消息,成员在 T+500ms 入群,分发后该成员没有投递。
  • 置信度:代码阅读确定。

[M-19] 记录保留期按创建时间算:长定时、长保留的消息一完成就被清掉

  • 严重级:low
  • 分类:与 PRD 不符(文档本身有歧义)
  • 现象与影响:清理条件是 created_at < now-7天。一条 30 天保留、第 29 天才被收下的消息,或者一条 365 天后的定时消息,完成后下一次清理就被删掉,后台看不到它的最终结果。
  • 证据:recover.go:81-89。
  • 文档依据:PRD F18「记录默认保留 7 天……设为 0 则完成后连记录一起删除」,隐含保留期从完成时开始算。
  • 为何不是故意设计:DEVIATIONS 里没有相关说明。
  • 解决方案:新增 completed_at 列(迁移),所有完成路径(配合 M-06 的统一函数)都写入它,清理按 completed_at 判断;同时在 DEVELOPMENT 7.6 写明口径。
  • 改动文件:internal/app/message/dispatch.go、recover.go;迁移文件(共享)。
  • 交互/冲突风险:后台记录查询可以顺带展示完成时间。
  • 需补测试:创建 30 天前、今天才完成的消息,清理后记录仍在。
  • 置信度:代码阅读确定。

复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。

**编号**:C-07 **严重级**:low **工作线**:消息核心(internal/app/message、serve 的 messageLoops) **来源**:审查 M-16、M-18、I-16、M-19 **依赖**:第 4 点依赖 C-03 (#34)、C-04 (#35) **被依赖**:无 ### 结论与统一方案 1. `keep` 时 `ttl_seconds<=0` 返回 `bad_request`;先判断 `*DelayMs > MaxScheduleSeconds*1000` 再做加法;`Send.Validate` 同步补上。(审查 M-16) 2. 写事务内检查发送方 `enabled`,已停用就拒绝,错误码建议 `unauthorized` 并记入 DEVIATIONS。(审查 M-18) 3. 群分发的接收者查询加 `AND gm.joined_at <= ?`(参数为 send_at),PRD F06"发送时刻之后才入群的端收不到"。(审查 I-16) 4. 新增 `completed_at` 列,所有完成路径(配合 C-04 的统一函数)写入,清理按 `completed_at` 判断,并在 DEVELOPMENT 7.6 写明口径。迁移号排在 C-03 的 0003 之后,或与 C-03 合并。(审查 M-19) ### 改动文件 `internal/app/message/submit.go`、`dispatch.go`、`recover.go`;`internal/protocol/validate.go`;迁移文件。 ### 与其他问题的交互 / 冲突说明 submit.go 与 C-05、U-03 改不同位置;第 4 点依赖 C-03、C-04。 ### 验收与测试 - 表驱动覆盖 ttl=0、ttl=-1、delay=MaxInt64。 - 停用后再提交被拒绝,且没有产生任何投递。 - 发送时刻为 T 的消息,成员在 T+500ms 入群,分发后该成员没有投递。 - 30 天前创建、今天才完成的消息,清理后记录仍在。 --- ### 问题明细(各区审查原文,证据含文件与行号) > 以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。**解决方案以本 issue 上方的"结论与统一方案"为准**;原文里的方案与之不一致时,按上方执行。 #### [M-16] 发送参数边界校验缺失:ttl_seconds 可以为 0 或负数,delay_ms 溢出后变成立即发送 - 严重级:low - 分类:协议一致性 - 现象与影响: - `keep:true` 配 `ttl_seconds<=0` 会被接受,`expire_at` 落在过去,结果取决于推送和清理谁先执行。 - 超大的 `delay_ms` 使 `nowMs+delay` 溢出成负数,绕过 `max_schedule_seconds` 检查,变成立即发送。 - 证据:`submit.go:52-54`(只查上限)、`submit.go:301`;`protocol/validate.go:57-65`。 - 文档依据:DEVELOPMENT 6.2 保留时长上限和定时上限;PRD F09、F11。 - 为何不是故意设计:DEVIATIONS 里没有相关说明。 - 解决方案:`ttl<=0` 返回 `bad_request`;先判断 `*DelayMs > MaxScheduleSeconds*1000` 再做加法;协议包的 `Send.Validate` 同步补上。 - 改动文件:`internal/app/message/submit.go`(以及协议包,属共享文件)。 - 交互/冲突风险:SDK 本地如有同类校验要保持一致。 - 需补测试:表驱动覆盖 ttl=0、ttl=-1、delay=MaxInt64。 - 置信度:代码阅读确定。 #### [M-18] 提交时不检查发送方是否已停用 - 严重级:low - 分类:逻辑 - 现象与影响:`Submit` 在事务内外都没有检查发送方的 `enabled`。停用写库完成到连接真正断开之间(发 fatal 后约 20 毫秒),上行队列里已经排着的 send 仍会被接受并投递;如果停用时恰好碰上重连(踢线没找到连接),窗口会更长。 - 证据:`submit.go:65-71`、`submit.go:203-211`。 - 文档依据:PRD F01「停用:立刻断开,不能再登录……它自己发出、还没推送出去的消息也作废」。 - 为何不是故意设计:DEVIATIONS 里没有相关说明。 - 解决方案:在写事务内检查 `snd.Enabled`,已停用就拒绝。错误码建议用 `unauthorized`,并记入 DEVIATIONS。 - 改动文件:`internal/app/message/submit.go`。 - 交互/冲突风险:与 identity 停用流程无冲突。 - 需补测试:停用后再提交被拒绝,且没有产生任何投递。 - 置信度:代码阅读确定。 #### [I-16] 群消息分发不按入群时间过滤(跨模块) - **严重级**:low - **分类**:与PRD不符 - **现象与影响**:分发时取的是分发那一刻的成员,没有 `joined_at <= send_at` 条件。分发由每秒一次的循环执行,每轮最多 100 条。在发送时刻和实际分发之间入群的人(通常不到 1 秒,积压时可达分钟级)会收到入群前发出的消息。 - **证据**:`message/dispatch.go:90-94`;`cmd/nixmsg/serve.go:322-333`。 - **文档依据**:PRD F06(210-211)「发送时刻之后才入群的端收不到」。 - **为何不是故意设计**:没有偏差记录,joined_at 字段本来就有。 - **解决方案**:接收者查询加上 `AND gm.joined_at <= ?`,参数为 send_at。 - **改动文件**:`message/dispatch.go`(消息线)。 - **与其他模块的交互/冲突风险**:退群后重新入群的人 joined_at 会更新,自然被排除,符合 PRD。 - **需补测试**:发送时刻为 T 的消息,成员在 T+500ms 入群,分发后该成员没有投递。 - **置信度**:代码阅读确定。 #### [M-19] 记录保留期按创建时间算:长定时、长保留的消息一完成就被清掉 - 严重级:low - 分类:与 PRD 不符(文档本身有歧义) - 现象与影响:清理条件是 `created_at < now-7天`。一条 30 天保留、第 29 天才被收下的消息,或者一条 365 天后的定时消息,完成后下一次清理就被删掉,后台看不到它的最终结果。 - 证据:`recover.go:81-89`。 - 文档依据:PRD F18「记录默认保留 7 天……设为 0 则完成后连记录一起删除」,隐含保留期从完成时开始算。 - 为何不是故意设计:DEVIATIONS 里没有相关说明。 - 解决方案:新增 `completed_at` 列(迁移),所有完成路径(配合 M-06 的统一函数)都写入它,清理按 `completed_at` 判断;同时在 DEVELOPMENT 7.6 写明口径。 - 改动文件:`internal/app/message/dispatch.go`、`recover.go`;迁移文件(共享)。 - 交互/冲突风险:后台记录查询可以顺带展示完成时间。 - 需补测试:创建 30 天前、今天才完成的消息,清理后记录仍在。 - 置信度:代码阅读确定。 --- <sub>复审基线:main `4059a15`(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。</sub>
nixevol added the P3-lowlane/messagereview-2026-09-30 labels 2026-09-30 13:57:00 +08:00
Author
Owner

已合入 origin/main 0c9b459。落地提交 659373e fix: 补提交校验、停用检查、入群过滤与按完成时刻清理 (#38)。

已合入 origin/main `0c9b459`。落地提交 `659373e` fix: 补提交校验、停用检查、入群过滤与按完成时刻清理 (#38)。
Sign in to join this conversation.