fix(api): align claim bounds across runtime snapshots
This commit is contained in:
+14
-8
@@ -96,9 +96,10 @@
|
||||
```
|
||||
|
||||
- 服务端解析并保存 canonical URL 与 `goods_id`;URL 非拼多多商品页、`goods_id` 缺失或含歧义则拒绝。
|
||||
- `title` 以 `TrimSpace` 后持久化值计,最多 120 个 Unicode code point;`sku_color`、`sku_size`
|
||||
同样按持久化值计,各最多 80 个 Unicode code point。非法 UTF-8 必须先拒绝,不能把替换字符当作
|
||||
合法 code point;超限不得截断。
|
||||
- `title` 以 Go `strings.TrimSpace`(Unicode White_Space)后的持久化值计,最多 120 个 Unicode code
|
||||
point;`sku_color`、`sku_size` 同样按持久化值计,各最多 80 个 Unicode code point。非法 UTF-8
|
||||
必须先拒绝,不能把替换字符当作合法 code point;为消除 Go 与 Python 默认 trim 差异,U+001C--U+001F
|
||||
四个 C0 分隔符无论位置一律拒绝;超限不得截断。
|
||||
- `goods_id` 只允许 1--32 位 ASCII 数字;规范金额只允许 1--32 个 ASCII 字符。
|
||||
- `max_total_price` 是本任务允许创建待付款订单的总额上限,不是参考单价。
|
||||
- 成功只产生 `DRAFT`;不得创建授权、开放设备领取或触发真机。
|
||||
@@ -228,7 +229,9 @@
|
||||
- 响应内 `title` 最多 120 个 Unicode code point,`sku_color` / `sku_size` 各最多 80 个;`goods_id`
|
||||
为 1--32 位 ASCII 数字,`max_total_price` 为最多 32 个 ASCII 字符的规范金额。创建、授权快照、
|
||||
candidate、持久化 claim snapshot 和 HTTP 输出共用同一合法域;既有畸形行只失败闭合,不迁移、
|
||||
截断或改写。最坏合法字段组合编码后必须明确小于既有 32 KiB claim 响应上限。
|
||||
截断或改写。candidate 查询必须先读取 task 与 authorization 两侧字段并分别验证;任一侧畸形必须
|
||||
回滚且不得持久化 `EMPTY`,只有两侧均合法但快照不一致时才跳过。最坏合法字段组合编码后必须明确
|
||||
小于既有 32 KiB claim 响应上限。
|
||||
- 一个设备最多有一个未关闭 claim。同 session 且租约有效时重放原 attempt;同一 attempt 已按服务端
|
||||
首事件原子进入 `ORDERING` 时也只在 task version 恰好为 claim 版本 +1 时恢复。不同 session、租约
|
||||
过期或业务状态异常固定返回 `409 {"error":"claim_requires_manual"}`,不释放、不转领、不新建 attempt。
|
||||
@@ -374,9 +377,11 @@ claim/renew 的格式错误固定为 `400 {"error":"invalid_request"}`,超限
|
||||
|
||||
### 文本和金额校验
|
||||
|
||||
- 标题/规格字段:输入按既有 `TrimSpace` 形成实际持久化值;title 最多 120 个 Unicode code point,
|
||||
颜色与尺码各最多 80 个。服务端先拒绝非法 UTF-8,再计 code point;不按 UTF-8 字节或视觉 grapheme
|
||||
计数,不截断超限值。规格比较仍为规范化后精确相等;不得包含、前缀、编辑距离或 AI 猜测。
|
||||
- 标题/规格字段:输入按 Go `strings.TrimSpace` 的 Unicode White_Space 集形成实际持久化值;title 最多
|
||||
120 个 Unicode code point,颜色与尺码各最多 80 个。服务端先拒绝非法 UTF-8,再计 code point;
|
||||
U+001C--U+001F 四个 C0 分隔符在任意位置均拒绝,客户端使用同一固定空白集而不依赖 Python
|
||||
`str.strip()` 默认语义。不按 UTF-8 字节或视觉 grapheme 计数,不截断超限值。规格比较仍为规范化后
|
||||
精确相等;不得包含、前缀、编辑距离或 AI 猜测。
|
||||
- `goods_id`:仅 1--32 位 ASCII 十进制数字,canonical URL 中唯一。
|
||||
- 金额:`0.01` 到系统配置上限,至多两位小数;规范化后必须是最多 32 个 ASCII 字符,再比较和持久化。
|
||||
- 数量:正整数,服务端与设备均设置合理上限;不能从字符串静默截断。
|
||||
@@ -402,7 +407,8 @@ events/fail/fence/result 或完整 `ResultSink`。T-304/T-306 必须通过 `Dura
|
||||
|
||||
金额按服务端合法域接受规范 ASCII 十进制正数字符串(最低 `0.01`,恰好两位小数、无前导零,最多
|
||||
32 个 ASCII 字符);`goods_id` 只接受 1--32 位 ASCII 数字,title 最多 120 个 Unicode code point,
|
||||
颜色与尺码各最多 80 个。客户端不得截断或修复漂移响应。wire 整数为正 int64,拒绝 bool。claim 成功
|
||||
颜色与尺码各最多 80 个。客户端用与 Go `strings.TrimSpace` 相同的固定 Unicode White_Space 集校验
|
||||
持久化文本,并与服务端共同拒绝任意位置的 U+001C--U+001F;不得截断或修复漂移响应。wire 整数为正 int64,拒绝 bool。claim 成功
|
||||
响应总上限仍为 32 KiB,最坏合法字段组合由双端契约测试证明严格小于该值。RFC3339Nano 按 0--9 位
|
||||
小数的纳秒时间轴比较,不能用 Python 微秒精度截断。
|
||||
|
||||
|
||||
+13
-5
@@ -22,7 +22,7 @@ write_paths:
|
||||
- client/tests/remote/**
|
||||
---
|
||||
|
||||
<!-- BEGIN VIKUNJA EXPORT id=55 synced=2026-08-04T17:25:11Z sha256=7fd613a5a64643deb5601136241868717a00cc9e7739c6f6a5bb2295e2a9210d -->
|
||||
<!-- BEGIN VIKUNJA EXPORT id=55 synced=2026-08-04T17:38:57Z sha256=7417ad9a21dacfee758c764866b37183a8a90e051c04cc5800e8d5c2a1ebf36e -->
|
||||
## 问题 / 背景
|
||||
|
||||
claim 成功响应受 32 KiB 总 body 上限保护,但创建、授权快照、candidate 和重放链路尚未统一约束各自由文本字段,客户端也未冻结同一单字段合法域。异常或历史超长值可能让合法业务事实变成不可传输响应。
|
||||
@@ -69,20 +69,28 @@ claim 成功响应受 32 KiB 总 body 上限保护,但创建、授权快照、
|
||||
### 2026-08-04T17:25:07Z · ila
|
||||
|
||||
2026-08-05 实现里程碑:服务端共享 validator 已覆盖创建持久化、start/authorization、candidate、claim snapshot/replay 和 handler 输出;客户端 wire 同界。最坏合法服务端 JSON 使用会被 escapeHTML 为 6 字节的 < 字符证明严格小于 32 KiB,客户端用 ensure_ascii=True 做保守预算证明。focused:Go tasks/taskclaim/server 全绿;client core 8/8、remote 15/15。
|
||||
|
||||
### 2026-08-04T17:38:44Z · ila
|
||||
|
||||
2026-08-05 审阅修复:统一 Go/Python 持久化文本域,客户端使用 Go Unicode White_Space 固定集,两端均拒绝任意位置 U+001C--U+001F,并补 NBSP/C0 回归。候选查询不再用 SQL 相等条件静默过滤:先读取 task 与 authorization 两侧快照并分别验证,单侧畸形立即回滚且不写 EMPTY/attempt;两侧均合法但不一致才按既有语义跳过。最坏合法 JSON 也覆盖 int64 版本编码上界。
|
||||
<!-- END VIKUNJA EXPORT -->
|
||||
|
||||
## 边界
|
||||
|
||||
- `title` 最多 120 个 Unicode code point,`sku_color` / `sku_size` 各最多 80 个 Unicode
|
||||
code point;边界以既有 `TrimSpace` 后实际持久化值为准。Go 必须先通过 `utf8.ValidString`,不能把
|
||||
code point;边界以 Go `strings.TrimSpace` 的 Unicode White_Space 集处理后的实际持久化值为准。
|
||||
客户端使用同一固定空白集,不依赖 Python `str.strip()` 默认语义;两端均拒绝任意位置的
|
||||
U+001C--U+001F 四个 C0 分隔符。Go 必须先通过 `utf8.ValidString`,不能把
|
||||
非法 UTF-8 解码出的 `RuneError` 当成合法 code point;不按 UTF-8 字节、不按视觉 grapheme,
|
||||
也不得为通过校验截断或规范化改写非空内容。
|
||||
- `goods_id` 只允许 1--32 位 ASCII 数字;canonical money 只允许 1--32 个 ASCII 字符,并继续
|
||||
满足既有正数、恰好两位小数、无非法前导零的规范格式。数量、版本与 generation 的正 int64
|
||||
边界保持不变,不能接受 bool、浮点数或字符串整数。
|
||||
- 创建任务、开始采购的授权快照、claim candidate、首次响应快照与幂等重放必须使用同一字段合法域;
|
||||
start/authorization 遇到既有畸形 task 必须原子失败,claim replay 还必须重新校验持久化响应快照。
|
||||
任一层发现超限或畸形既有数据都失败闭合,不迁移、不截断、不覆盖或修复历史行。
|
||||
- 创建任务、开始采购的授权快照、claim candidate、首次响应快照与幂等重放必须使用同一字段合法域。
|
||||
candidate 必须先读取并分别校验 task 与 authorization 两侧字段;任一侧畸形立即回滚且不写 `EMPTY`,
|
||||
只有两侧均合法但快照不一致时才跳过。start/authorization 遇到既有畸形 task 必须原子失败,claim
|
||||
replay 还必须重新校验持久化响应快照。任一层发现超限或畸形既有数据都失败闭合,不迁移、不截断、
|
||||
不覆盖或修复历史行。
|
||||
- claim 成功响应继续受既有 32 KiB HTTP body 上限约束;最坏合法字段组合的编码结果必须由契约测试
|
||||
明确证明小于该上限。HTTP handler 在序列化前重验完整 claim response,并拒绝任何越界 service
|
||||
输出;不得调高响应上限掩盖无界字段。
|
||||
|
||||
Reference in New Issue
Block a user