From bb5e3b973fcdcbd9dde5e11505e761de2ef70d0c Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 8 Aug 2026 07:52:18 +0000 Subject: [PATCH] =?UTF-8?q?docs(fsys):=20=E4=BF=AE=E6=AD=A3=20README=20?= =?UTF-8?q?=E7=BC=93=E5=AD=98/=E5=85=BC=E5=AE=B9=E6=80=A7=E6=8E=AA?= =?UTF-8?q?=E8=BE=9E=EF=BC=8C=E6=B5=8B=E8=AF=95=E9=A1=B5=E8=A1=A5=20video?= =?UTF-8?q?=20Range=20=E7=94=A8=E4=BE=8B=EF=BC=8C=E5=AE=A1=E8=AE=A1?= =?UTF-8?q?=E6=8A=A5=E5=91=8A=E5=8B=98=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/security/fsys_SECURITY_AUDIT_20260808.md | 4 ++++ fsys/README.md | 4 ++-- fsys/public/index.html | 12 ++++++++++++ fsys/public/test/sample.mp4 | Bin 0 -> 1024 bytes 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 fsys/public/test/sample.mp4 diff --git a/docs/security/fsys_SECURITY_AUDIT_20260808.md b/docs/security/fsys_SECURITY_AUDIT_20260808.md index ad25cdd..2647033 100644 --- a/docs/security/fsys_SECURITY_AUDIT_20260808.md +++ b/docs/security/fsys_SECURITY_AUDIT_20260808.md @@ -27,6 +27,7 @@ fsys 是 Python `static` 模块的 C++20 重实现(libhttp + libmsg + boost::j - **位置**: `load_config()` 步骤 8(原 L347-369) - **问题**: 相对路径 `static_root` 通过 `/proc/self/exe` 基于可执行文件目录解析。二进制位于 `fsys/build/fsys`,而 `public` 符号链接位于 `fsys/public`,因此 `./public` 解析为 `fsys/build/./public`(不存在)。更严重的是构造函数 `fs::create_directories()` 会**静默创建空目录**,服务启动正常但所有路由返回 404/500,无告警。 - **修复**: 相对路径改为基于 `fs::current_path()` 解析(`_run.sh` 以模块目录为 cwd 启动,与 Python 版 `BASE_DIR` 语义一致;Docker 使用绝对路径 `STATIC_ROOT=/app/public`,不受影响)。SSL 证书路径同样修复。 +- **勘误(2026-08-08 后)**: 原描述中 `fsys/public` 为指向 `../static/public` 的符号链接;现已改为真实目录,内含自检测试页(`index.html` 等),不再与 static 共享内容。 ### F-2: favicon 代理 `CURLOPT_FOLLOWLOCATION` 盲跟重定向绕过 SSRF 校验(回归) ✅ 已修复 @@ -191,6 +192,9 @@ macOS/Linux 双开发环境约定,Linux 编译需覆盖 `INCS/LIBS/RPATH`( | `POST /api/hash` | ✓ 哈希值与 Python 版逐位一致 | | `POST /api/check_user` | ✓ 与 Python 版行为一致(当前环境 db Users 查询失败,两版同返 500 query failed,非 fsys 缺陷) | | dnfs 代理 `/get_notes` | ✓ 透传(上游自身 500,直连上游同果,非代理缺陷) | +| `/video/*` 无 Range 200 / Range 206(Content-Range/Content-Length)/ 越界 416 | ✓(2026-08-08 补测,见下) | + +> **补充(2026-08-08)**: 初轮冒烟未覆盖 `/video/*` Range 路径(当时无测试媒体文件)。后在 `fsys/public/` 内置自检测试页(`index.html`,15 项自动检查)及 `test/sample.mp4`(1KB),补齐 Range 用例:全量 200 + `Accept-Ranges: bytes`、`bytes=0-99` → 206 + `Content-Range: bytes 0-99/1024` + `Content-Length: 100`、`bytes=999999-` → 416,均验证通过(F-4 的 416 分支同时覆盖)。 --- diff --git a/fsys/README.md b/fsys/README.md index c042ccf..749411c 100644 --- a/fsys/README.md +++ b/fsys/README.md @@ -2,7 +2,7 @@ 基于 libhttp + libmsg 的 C++20 静态文件托管与前端页面服务,监听 `127.0.0.1:9200`。 -Python/Flask `static` 模块的生产级 C++ 重写,接口 100% 兼容,前端零改动。 +Python/Flask `static` 模块的生产级 C++ 重写,前端零改动兼容(改进性差异:上游状态码透传使前端 401 跳转首次生效,详见审计报告 D-1)。 ## 架构 @@ -39,7 +39,7 @@ fsys :9200 (libhttp 异步多线程 + CORS) |------|------| | `POST /api/hash` | SHA-256 密码哈希(限速 20/min/IP) | | `POST /api/check_user` | 检查用户是否已注册(限速 10/min/IP) | -| `GET /api/favicon?url=` | favicon SSRF 防护代理(512KB 上限,1 天缓存) | +| `GET /api/favicon?url=` | favicon SSRF 防护代理(512KB 上限,仅浏览器侧 Cache-Control 缓存 1 天,无服务端缓存) | ### dnfs2 代理路由 diff --git a/fsys/public/index.html b/fsys/public/index.html index 3e51790..aaf9090 100644 --- a/fsys/public/index.html +++ b/fsys/public/index.html @@ -42,6 +42,7 @@ GET /api/filelist?path=test%2F(编码子目录)— GET /api/filelist?path=..%2F..(遍历应 403)— GET /test/hello.txt(静态文件)— + GET /video/test/sample.mp4(Range 206/416)— GET /test/hello.txt?download=true(附件头)— GET 中文文件名(URL 解码)— POST /api/hash(sha256("test123"))— @@ -109,6 +110,17 @@ const tests = { const ok = res.status === 200 && text.includes('hello fsys'); setRow('static_file', ok, `HTTP ${res.status}, ${(res.headers.get('content-type') || '').split(';')[0]}`); }, + async video_range() { + const full = await fetch('/video/test/sample.mp4'); + const partial = await fetch('/video/test/sample.mp4', { headers: { Range: 'bytes=0-99' } }); + const bad = await fetch('/video/test/sample.mp4', { headers: { Range: 'bytes=999999-' } }); + const cr = partial.headers.get('content-range') || ''; + const ok = full.status === 200 && full.headers.get('accept-ranges') === 'bytes' + && partial.status === 206 && cr.startsWith('bytes 0-99/') + && partial.headers.get('content-length') === '100' + && bad.status === 416; + setRow('video_range', ok, `full=${full.status}, range=${partial.status} ${cr}, bad=${bad.status}(期望 416)`); + }, async download() { const { res } = await j('/test/hello.txt?download=true'); const cd = res.headers.get('content-disposition') || ''; diff --git a/fsys/public/test/sample.mp4 b/fsys/public/test/sample.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..dee0bb26b8d3c1fa287e6376af2d1ba60bde8c6f GIT binary patch literal 1024 zcmV+b1poUa;MAYu6)b9_OzDDi)>Ld56Iv&TqomgRVMN2lEtvcbrzH^qOZdb?GV-z9 zK)mP1N%3JmuxInP6!-|S9pXL~NalM`X5^4^_y*v)^qx*Iwy^cX=41P*s0NfhGgYk! z0?q4V6oO`R6j!`|b15wxlzkFR8_&tC(J*?Vgz<`o57mthA-dWi+dM`MzsbG1goME@ zWqa%EhX)FLho3iz;F0VqvifE$s zL~Dg-UUAz!wSSYBy=p>2XRkZM8}ebLk&ho)6-^7fFed~(C5ju`l)XzT0z4bPnJF+_ z{^FseyVd1y`7rqRGW~gYvAX8_b-)HwG;AnX;0>5*X>R{C!nneX=Y*TZ$s6YfChVsB z2;*TcF!P;bN5R-5CKj2<<=?E`Ia)le964WQ9#bnzsp4x)Q!4$4HeTEz9Nvf~Dz4`X zRS6#r+X}%=Xv|uG+UbXd8LXx>r8;;pqezyst^ z-h5V@!J5w*`JAyMtP)KRrF@EM%bZS4oiJX{S{y-n&E;WnGlcERjeRUtyg1W=GJLb| zYaVl$ol*%Nbqsmj;se*%8mP+L1}!9UBZf>v0peFgd@YHK!l+y{^a+Mjmkgzo+0rfN zJ}rlc7(2?2?oog+)^|-2&@E+8Z3vw*A-Fe0e8g}$fjI716su`b7;hz}XA8nK+r5FV zo+y8u)Sq)?wrR(qLd;PvNYtijx&Y|2ApMpK&uNbvlSefoRLj|W@eR0}=@5ahN7P_V zlXt065FT);H%co-5rPm~cJS0_;VKkZz4s^i-|re{zw!Jyh*JEp`?{Ilf)9cQPnZ;f z=83=kuA)eh$l#SZ1x{NkaftrnfAk5Go}eGswmA6XrpekRp}VKHMjPzMjjyIHvHB}l z+qT4exwm^O7VdB#?!9zwm$ u0AglmlvD}r(Ax9BRyf^L$11sj45i8LHpyPCD9<%JCO9R<82~!a@Q*@vV)~{4 literal 0 HcmV?d00001