feat(安全): 对外暴露面加固 + 界面去 AI 化(v1.5.0)
界面(去 AI 味): - 大屏页清除 114 处生成器残留属性 data-page-node-id - 视觉系统改回工程控制台风格:去 radial/linear-gradient、去辉光、 去标题前彩色装饰条,改为中性灰阶 + 单一蓝色强调色;KPI 色条改状态点 - 精简各页说教式长提示;修掉 profile.html 泄漏到页面上的 Markdown 星号 - 删除登录页过时的「默认账号 admin / admin123」提示(1.4.0 起已无默认口令) 安全与隐私(按「将会被公网访问」收口): - 内部异常只回 8 位事件号,完整堆栈进服务端日志(web/api.py::_internal) - 导出文件名收敛:防响应头注入与路径穿越;manage.py passwd 补用户名校验 - 登录对不存在的账号也走一次哑哈希,抹平用户名枚举的时序差异 - /api/* 读接口限速 240 次 / 60 秒 / 账号(挡住循环调 /api/bundle) - 进程 umask 0077 + 目录 0700 / 文件 0600:对话正文与主密钥的落盘权限 - 表名与库文件路径只对管理员下发;大屏页所有数据插值转义 - --debug 只允许绑定回环地址;新增 Permissions-Policy 与 413 处理器 文档: - DEPLOYMENT 新增第十三节「安全与隐私基线」;迁移表补 1.4.0 → 1.5.0 行 - SECURITY 更新支持范围、新增「信息泄漏收敛」小节与上线检查项 - .codebuddy/ 加入 .gitignore(助手工作记忆不进仓库) 版本:1.4.0 → 1.5.0(无库结构变更,user_version 仍为 4) 验证:python tools/smoke.py → ok=264 fail=0;python tools/check_docs.py → 0 处问题
这个提交包含在:
+13
-2
@@ -21,6 +21,7 @@
|
||||
"""
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
@@ -484,11 +485,20 @@ def export_csv(conn, uid, path=None, username=None):
|
||||
|
||||
文件名带账号名:多用户下所有人导出到同一个目录,
|
||||
不带归属就会互相覆盖。
|
||||
|
||||
账号名必须收敛后才能拼进路径:`manage.py passwd` 建号时**不校验**用户名,
|
||||
老库升级上来的名字也可能带 `/` 或 `..`。直接拼接就是路径穿越 ——
|
||||
一个叫 `..\\..\\x` 的账号能把 CSV 写到数据目录之外。
|
||||
这里只保留 [A-Za-z0-9._-],结果固定落在 EXPORT_DIR 之下。
|
||||
|
||||
另:导出文件里是**对话正文的明文**,所以落盘后立刻收紧权限
|
||||
(进程 umask 已经收到 0077,这里是针对「文件早已存在、权限是旧的」那种情况)。
|
||||
"""
|
||||
if path is None:
|
||||
tag = username or ("u%s" % uid)
|
||||
tag = re.sub(r"[^A-Za-z0-9._-]", "_", str(username or ""))[:48].strip("._-")
|
||||
tag = tag or ("u%s" % uid)
|
||||
path = os.path.join(config.EXPORT_DIR, "usage_records_%s.csv" % tag)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
||||
rows = conn.execute("SELECT request_id,credits,prompt,model,client,ts FROM usage_records "
|
||||
"WHERE user_id=? ORDER BY ts, request_id", (uid,))
|
||||
n = 0
|
||||
@@ -499,6 +509,7 @@ def export_csv(conn, uid, path=None, username=None):
|
||||
w.writerow([r["request_id"], "%.2f" % r["credits"], r["prompt"] or "",
|
||||
r["model"], r["client"], r["ts"]])
|
||||
n += 1
|
||||
config.harden_file(path)
|
||||
return path, n
|
||||
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户