Files
rond-with-you/internal/fog/limit_test.go
T
tamakyi b4c2a8452f 审计修复:后端(安全 / 数据一致性 / 性能)
audit-2026-09-21.md 里的后端条目落地。每条都配了回归守卫,并逐条做过变异验证
(注入原 bug → 确认按正确理由变红 → 还原 → 变绿)。

安全
- CSRF 只加同源 Origin/Referer 守卫(csrf.go),不动模板
- 会话 cookie 的 Secure 按请求判断;两步验证两闸(loginGuard + 验证码一次性)
- 跳转收口到 safeInternalPath;clientIP 只认 XFF 最后一段
- GPX 按属主圈定且查 RowsAffected;下载名走 downloadDisposition
- 上传口统一 MaxBytesReader;落盘后的失败路径清掉半成品

数据一致性
- 区域判定两端各过一遍再 AND(apiTrack 与 movementsWhere 对齐)
- Filter.Verdict 全域一致(下推到 visitsWhere);聚合刷新必须返回 error
- 附属数据改挂坐标键,Go 与 SQL 两套表达式逐位一致
- 脏备份的伪时间/越界坐标不再进领域模型;rows 循环统一 drainRows
- 合并与导出:writeRawRow 上抛错误、悬空引用清理、原生列补全

性能
- placeCols 相关子查询带 dataset_id 前导列;findSourceArchive 先按大小筛
- 批量勾选改 ANY($3::bigint[]);fillupCache 加上限;同一查询不再查两遍

另:新增「全新库」冒烟测试 —— 空库跑完 Migrate 再用应用侧读查询真打一遍,
迁移块排到依赖表之前这种错只有新库会炸,老库毫无症状。
2026-09-24 20:37:16 +08:00

79 lines
2.2 KiB
Go

package fog
import (
"archive/zip"
"bytes"
"compress/zlib"
"strings"
"testing"
)
// TestZlibDecompressRejectsBomb 守住「zlib 流能解出任意长度」这个口子。
// 几十 MB 的全零数据压出来只有几十 KB,解压时不设上限就是一个标准 zip bomb——
// 上传接口只有站长能用,但 1C1G 的机器被打爆一次就够受。
func TestZlibDecompressRejectsBomb(t *testing.T) {
var buf bytes.Buffer
zw := zlib.NewWriter(&buf)
zeros := make([]byte, 1<<20)
written := 0
for written <= maxTileZBytes {
if _, err := zw.Write(zeros); err != nil {
t.Fatal(err)
}
written += len(zeros)
}
if err := zw.Close(); err != nil {
t.Fatal(err)
}
t.Logf("构造出 %d 字节的 zlib 流,解压后 %d 字节", buf.Len(), written)
if _, err := zlibDecompress(buf.Bytes()); err == nil {
t.Errorf("解压出 %d 字节(上限 %d)的流应当被拒绝", written, maxTileZBytes)
} else {
t.Logf("已拒绝: %v", err)
}
}
// TestZlibDecompressNormalStream 反向确认:正常大小的流仍能原样解出,
// 免得上面那条把限额做得太紧、把真快照也一起挡了。
func TestZlibDecompressNormalStream(t *testing.T) {
var buf bytes.Buffer
zw := zlib.NewWriter(&buf)
want := strings.Repeat("rond", 4096)
if _, err := zw.Write([]byte(want)); err != nil {
t.Fatal(err)
}
zw.Close()
got, err := zlibDecompress(buf.Bytes())
if err != nil {
t.Fatalf("正常流不该被拒: %v", err)
}
if string(got) != want {
t.Errorf("解压结果不一致: %d 字节 → %d 字节", len(want), len(got))
}
}
// TestDecodeRejectsOversizedTileEntry 确认 Decode 这一层也限住了「压缩后」的体积:
// 条目本身就超限时直接报错,而不是先整个读进内存再判断。
func TestDecodeRejectsOversizedTileEntry(t *testing.T) {
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
w, err := zw.Create("Model/*/" + tileName(42))
if err != nil {
t.Fatal(err)
}
if _, err := w.Write([]byte(strings.Repeat("x", maxTileRawBytes+1))); err != nil {
t.Fatal(err)
}
if err := zw.Close(); err != nil {
t.Fatal(err)
}
if _, _, err := Decode(buf.Bytes()); err == nil {
t.Errorf("压缩后超过 %d 字节的瓦片应当被拒绝", maxTileRawBytes)
} else {
t.Logf("已拒绝: %v", err)
}
}