v3.6.1 安全修复同步发版

- 盘面图 XSS 防护:装配层全面转义用户可控字段 + 模板严格 CSP(default-src 'none',无 script-src)+ 6 项注入向量回归测试
- 自更新签名信任锚:--apply 双源 manifest 交叉验证 → ed25519 验签(纯 Python 实现,缺 sig 即拒)→ 逐文件 SHA256,全 fail-closed
- sync 工具同步后自动重建 manifest + 签名 + 独立验签,私钥缺失 fail-closed
- 测试 170 项全绿
This commit is contained in:
2026-10-10 16:21:30 +08:00
parent 433c9b4a41
commit 415cb472b1
13 changed files with 805 additions and 41 deletions
+13 -1
View File
@@ -13,6 +13,7 @@ logo 与品牌字体(render/assets/)以 base64 内嵌,保证 HTML 单文
import base64
import re
from datetime import datetime
from html import escape as _escape
from pathlib import Path
from chart.ganzhi import NAYIN, WX_GAN, WX_ZHI, gz_void, ten_god, terrain
@@ -32,6 +33,14 @@ _SEASON_LABEL = ("旺", "相", "休", "囚", "死")
_ASSETS_DIR = Path(__file__).resolve().parent / "assets"
_SHA_NOTE_RE = re.compile(r"[((][^))]*[))]")
# 结构性占位符:值本身是装配层渲染出的 HTML 片段或 base64 资源,
# 填充时原样注入;其余占位符均为文本字段,一律 HTML 转义防注入。
_STRUCTURAL_KEYS = frozenset({
"LOGO_B64", "FONT_B64", "QRCODE_B64",
"WARNINGS", "MAIN_TABLE", "LUCK_STRIP", "CHILDHOOD_BLOCK",
"FLOW_YEARS", "FLOW_MONTHS",
})
def _strip_sha_note(entry: str) -> str:
"""神煞条目剔除查法括号注释:`将星(以年支子查,见于月支子)` → `将星`。"""
@@ -239,5 +248,8 @@ def build_html(chart) -> str:
}
out = tpl
for k, v in mapping.items():
out = out.replace("{{" + k + "}}", str(v))
v = str(v)
if k not in _STRUCTURAL_KEYS:
v = _escape(v, quote=True)
out = out.replace("{{" + k + "}}", v)
return out