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 处问题
这个提交包含在:
+18
-1
@@ -25,7 +25,7 @@ from flask import Flask, jsonify, render_template, request
|
||||
|
||||
from . import config, db, security
|
||||
|
||||
__version__ = "1.4.0"
|
||||
__version__ = "1.5.0"
|
||||
PROJECT_NAME = config.PROJECT_NAME
|
||||
|
||||
|
||||
@@ -44,6 +44,10 @@ def _setup_logging(app):
|
||||
|
||||
|
||||
def create_app(start_scheduler=True, do_init_db=True, **overrides):
|
||||
# 必须在任何写盘动作之前:把进程 umask 收到 0077,此后新建的 SQLite 库、
|
||||
# -wal / -shm、导出 CSV、备份 zip、日志文件一律是 owner-only。
|
||||
# 数据目录里躺着对话正文与主密钥,对外部署时同机其它用户不该读得到。
|
||||
config.harden_process()
|
||||
app = Flask(__name__,
|
||||
template_folder="web/templates",
|
||||
static_folder="web/static",
|
||||
@@ -100,6 +104,19 @@ def create_app(start_scheduler=True, do_init_db=True, **overrides):
|
||||
return jsonify({"ok": False, "error": "forbidden", "message": "没有权限"}), 403
|
||||
return render_template("error.html", code=403, message="没有权限"), 403
|
||||
|
||||
@app.errorhandler(413)
|
||||
def _413(e):
|
||||
"""请求体过大(MAX_CONTENT_LENGTH)。
|
||||
|
||||
不加这一层的话 Flask 会吐一个默认 HTML 页,而接口调用方拿到的是
|
||||
HTML 而不是 JSON —— 前端 `r.json()` 会抛异常,最后在界面上表现成
|
||||
「请求失败:Unexpected token <」。这里统一成与其它错误一致的形状。
|
||||
"""
|
||||
msg = "请求体过大(上限 %d MB)" % (app.config["MAX_CONTENT_LENGTH"] // (1024 * 1024))
|
||||
if request.path.startswith("/api/"):
|
||||
return jsonify({"ok": False, "error": "too_large", "message": msg}), 413
|
||||
return render_template("error.html", code=413, message=msg), 413
|
||||
|
||||
@app.errorhandler(500)
|
||||
def _500(e):
|
||||
app.logger.exception("内部错误")
|
||||
|
||||
在新工单中引用
屏蔽一个用户