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 再用应用侧读查询真打一遍, 迁移块排到依赖表之前这种错只有新库会炸,老库毫无症状。
79 lines
2.2 KiB
Go
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)
|
|
}
|
|
}
|