审计修复:前端(模板 / CSS / JS 与无障碍)

- 地图脚本:localStorage 读写走 lsGet/lsSet 兜底(隐私模式下裸访问会打断整段脚本);
  preview() 在 /api/map-config 还没回来时不丢点(previewQueue 在 if (live) 之前排队);
  异步回包一律发序号作废,取消后回来的包不再画
- photo-exif.js:EXIF 时间全 0 不再变成 1899-11-30 写进到访时间;读 count 前先按版本卡宽度
- 数据编辑页:三个筛选表单都把其余条件用隐藏域原样带上,提交不再把别的列表的筛选清掉
- 补全页:sessionStorage 的读写一律包 try(脏数据/隐私模式都会抛)
- 窄屏:删掉被后面同特异性规则盖掉的死内边距;宽表堆叠类名成对、判据扩到所有 tb 表
- 无障碍:图片删除按钮补 aria-label,访客画廊图片补 alt
- report.html 补上漏掉的 </tr>,fog-mask 的宽表带 tb-stack

本批不碰数据库与导出路径:往返包 823/323/8/30/791/557/2246 全等、
坐标偏差 0.000000000、时间偏差 0s、迷雾 8701 块一致。
This commit is contained in:
tamakyi
2026-09-24 20:37:57 +08:00
parent b4c2a8452f
commit d5f6dfa381
18 changed files with 675 additions and 77 deletions
+57
View File
@@ -0,0 +1,57 @@
package web
import (
"strings"
"testing"
)
// mapHTMLSinks 是「可编辑字段被裸拼进 HTML」的具体写法。这些值来自地点名、交通方式名、
// 城市名、GPX 轨迹名——站长自己可编辑,但访客页也会一起渲染。
//
// Leaflet 的 bindTooltip / bindPopup 把字符串参数按 **HTML** 解析,innerHTML 同理,
// 所以少一层 esc() 就是一次注入。同文件里的 popup() 早就用 esc() 了,
// 这四处漏掉纯属不一致(2026-09-21 审计 P1-4)。
var mapHTMLSinks = []string{
"bindTooltip(p.n", // 地点名
"bindTooltip((s.m", // 交通方式名(轨迹 tooltip)
"bindTooltip(t.name", // GPX 轨迹名
"'<b>' + c.city", // 城市名(城市气泡)
"+ m.name +", // 交通方式名(图例)
"+ (m.color ||", // 交通方式的颜色值,进了 style 属性
}
// TestMapTooltipsEscapeEditableFields 守住地图 JS 的 HTML 出口:
// 1. 已知的裸拼接写法一个都不许出现;
// 2. 每一个 bindTooltip / bindPopup 的调用行要么走 esc(),要么传的是 function(自己建 DOM)。
//
// 判据放在源码文本上而不是浏览器里,是因为这四处的注入后果很轻(站长可编辑的数据偏「自伤」),
// 但渲染错乱是实打实的——用 CDP 起浏览器只为了证明一句 esc() 太贵。
func TestMapTooltipsEscapeEditableFields(t *testing.T) {
raw, err := staticFS.ReadFile("static/js/rond-map.js")
if err != nil {
t.Fatalf("读取 rond-map.js 失败: %v", err)
}
src := string(raw)
for _, bad := range mapHTMLSinks {
if strings.Contains(src, bad) {
t.Errorf("rond-map.js 里出现未转义的 HTML 拼接 %q:该字段可编辑,须过 esc()", bad)
}
}
checked := 0
for i, line := range strings.Split(src, "\n") {
if !strings.Contains(line, ".bindTooltip(") && !strings.Contains(line, ".bindPopup(") {
continue
}
checked++
if !strings.Contains(line, "esc(") && !strings.Contains(line, "function") {
t.Errorf("rond-map.js:%d 的 tooltip/popup 参数既没过 esc() 也不是 function,"+
"Leaflet 会按 HTML 渲染:%s", i+1, strings.TrimSpace(line))
}
}
if checked == 0 {
t.Fatal("一个 bindTooltip/bindPopup 都没扫到:写法变了,这条守卫就失效了")
}
t.Logf("扫了 %d 处 tooltip/popup", checked)
}