From 13be4bd8ca72bd393e44a9bb5819a4f7d902f8f7 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 09:58:25 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E9=97=AE=E9=A2=98=EF=BC=8C=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=88=AB=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/src/main.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gateway/src/main.cpp b/gateway/src/main.cpp index 7a90b26..c37ce5c 100644 --- a/gateway/src/main.cpp +++ b/gateway/src/main.cpp @@ -304,8 +304,10 @@ static std::mutex _CAPTCHA_MTX; std::string generate_captcha_text() { static const char chars[] = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789"; - static std::mt19937 rng(std::random_device{}()); - static std::uniform_int_distribution dist(0, sizeof(chars) - 2); + // thread_local:每个线程独立 rng 实例,避免多线程并发调用 dist(rng) 时的数据竞争 + // (static 变量初始化本身线程安全,但 mt19937::operator() 修改内部状态非线程安全) + thread_local std::mt19937 rng(std::random_device{}()); + thread_local std::uniform_int_distribution dist(0, sizeof(chars) - 2); std::string text; for (int i = 0; i < 5; i++) { @@ -717,6 +719,10 @@ http::response handleRegister( // IP rate limit check std::string client_ip = "direct"; + auto it_host = req.base().find("X-Forwarded-For"); + if (it_host != req.base().end()) { + client_ip = std::string(it_host->value()); + } if (!_REGISTER_RATE.check_and_record(client_ip, 3, 600)) { json::value jv_r = { {"success", false}, -- 2.54.0 From efae81b2304262f4cd4f89d6fdaac1c2b8b94ad7 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 10:05:24 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=B3=84=E6=BC=8F=E4=BB=A5=E5=8F=8Atoken?= =?UTF-8?q?=E6=97=A0=E9=99=90=E6=9C=9F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/inc/ExpBase.hpp | 19 ++++++++++++++----- gateway/inc/LocalTime.hpp | 3 ++- gateway/src/main.cpp | 37 ++++++++++++++++++++++++++++++------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/gateway/inc/ExpBase.hpp b/gateway/inc/ExpBase.hpp index 8531cf6..c2509c6 100644 --- a/gateway/inc/ExpBase.hpp +++ b/gateway/inc/ExpBase.hpp @@ -161,15 +161,23 @@ protected: virtual std::string build_message() const { std::string result; - + // 错误码 if (m_code != 0) { result += "[Error " + std::to_string(m_code) + "] "; } - + // 错误消息 result += m_message; - + + /* ===== 以下「位置信息」与「嵌套异常」暂时以注释方式保留 ===== + * 原因:黑盒检测发现 what() 的返回值会透传到 HTTP 响应体, + * 导致泄露源码文件路径、行号、C++ 模板类型签名(函数名), + * 便于攻击者定位代码、确认技术栈、构造针对性 payload(P0-3)。 + * 排障:完整信息仍由各 handler 通过 std::cerr 输出到服务端日志。 + * 恢复:如需调试可临时取消下方注释;生产环境必须保持注释状态, + * 并确保 what() 不直接进入客户端响应。 + * ==================================================================== // 位置信息 if (!m_file.empty()) { result += " (at " + m_file; @@ -181,12 +189,13 @@ protected: } result += ")"; } - + // 嵌套异常 if (m_nested) { result += "\n Caused by: " + std::string(m_nested->what()); } - + * ==================================================================== */ + return result; } diff --git a/gateway/inc/LocalTime.hpp b/gateway/inc/LocalTime.hpp index ca5cd42..be7d415 100644 --- a/gateway/inc/LocalTime.hpp +++ b/gateway/inc/LocalTime.hpp @@ -1,3 +1,4 @@ +#pragma once #include #include #include @@ -13,7 +14,7 @@ public: auto now = system_clock::now(); auto zone = current_zone(); zoned_time zt{zone, now}; - return std::vformat(format, std::make_format_args(zt)); + return std::vformat("{:" + format + "}", std::make_format_args(zt)); } // 解析时间字符串为 system_clock::time_point(本地时间 → UTC) diff --git a/gateway/src/main.cpp b/gateway/src/main.cpp index c37ce5c..c9ba0c4 100644 --- a/gateway/src/main.cpp +++ b/gateway/src/main.cpp @@ -697,9 +697,14 @@ http::response handleLogin( catch (const HttpException& e) { + // 详细错误信息(e.what() 含源码路径/行号/C++ 模板类型)暂时以注释方式保留 + // 原因:黑盒检测发现透传到客户端会泄露内部实现,便于攻击者定位代码(P0-3) + // 排障:完整信息仍输出到服务端 stderr + std::cerr << "[handleLogin HttpException] " << e.what() << std::endl; json::value jv_r = { {"success", false}, - {"message", e.what()} + /* {"message", e.what()}, */ // 泄露源码信息,已禁用 + {"message", "Invalid Username or Password"} }; res.set(http::field::content_type, "application/json"); res.result(e.status()); @@ -799,9 +804,12 @@ http::response handleRegister( } catch (const HttpException& e) { + // 详细错误信息(e.what() 含源码路径/行号/C++ 模板类型)暂时以注释方式保留(同 handleLogin) + std::cerr << "[handleRegister HttpException] " << e.what() << std::endl; json::value jv_r = { {"success", false}, - {"message", e.what()} + /* {"message", e.what()}, */ // 泄露源码信息,已禁用 + {"message", "Registration failed"} }; res.set(http::field::content_type, "application/json"); res.result(e.status()); @@ -810,9 +818,19 @@ http::response handleRegister( catch (const std::exception& e) { const auto& ti = typeid(e); - res.set(http::field::content_type, "text/plain"); + // 详细错误信息(ti.name() 含 mangled 类型名、e.what() 含内部细节)暂时以注释方式保留 + // 原因:mangled 类型名会确认技术栈与 STL 容器,e.what() 可能泄露内部路径(P0-3) + // 排障:完整信息仍输出到服务端 stderr + std::cerr << "[handleRegister std::exception] <" << ti.name() << "> " << e.what() << std::endl; + res.set(http::field::content_type, "application/json"); res.result(http::status::internal_server_error); - res.body() = std::string("<") + ti.name() + "> " + e.what(); + json::value jv_r = { + {"success", false}, + /* {"err_name", ti.name()}, // 泄露 C++ 类型信息,已禁用 */ + /* {"err_message", e.what()} // 泄露内部细节,已禁用 */ + {"message", "Internal Server Error"} + }; + res.body() = json::serialize(jv_r); } res.prepare_payload(); @@ -940,12 +958,17 @@ http::response handleProxy( catch (const std::exception& e) { const auto& ti = typeid(e); - res.set(http::field::content_type, "text/plain"); + // 详细错误信息(ti.name() 含 mangled 类型名、e.what() 含内部细节)暂时以注释方式保留 + // 原因:mangled 类型名会确认技术栈与 STL 容器,e.what() 可能泄露内部路径(P0-3) + // 排障:完整信息仍输出到服务端 stderr + std::cerr << "[handleProxy std::exception] <" << ti.name() << "> " << e.what() << std::endl; + res.set(http::field::content_type, "application/json"); res.result(http::status::internal_server_error); json::value jv_r = { {"success", false}, - {"err_name", ti.name()}, - {"err_message", e.what()} + /* {"err_name", ti.name()}, // 泄露 C++ 类型信息,已禁用 */ + /* {"err_message", e.what()} // 泄露内部细节,已禁用 */ + {"message", "Internal Server Error"} }; res.body() = json::serialize(jv_r); } -- 2.54.0 From bfc361ddfb33006a47d18f583fd1afd079478115 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 10:08:02 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E4=B9=8B=E5=89=8D=E7=9A=84=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=9C=89=E9=81=97=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/src/main.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gateway/src/main.cpp b/gateway/src/main.cpp index c9ba0c4..a28558a 100644 --- a/gateway/src/main.cpp +++ b/gateway/src/main.cpp @@ -525,11 +525,17 @@ catch (const std::exception& e) { http::response res; res.version(req.version()); const auto& ti = typeid(e); + // 详细错误信息(ti.name() 含 mangled 类型名、e.what() 含内部细节)暂时以注释方式保留 + // 原因:此处为 wrapperHttp 外层 catch,会捕获 handler 内未处理的 std::exception + // 透传到客户端会泄露 C++ 类型信息与内部路径(P0-3 遗漏点,已补全) + // 排障:完整信息仍输出到服务端 stderr + std::cerr << "[wrapperHttp std::exception] <" << ti.name() << "> " << e.what() << std::endl; json::value jv_r = { {"success", false}, - {"message", std::string("<") + ti.name() + "> " + e.what()} + /* {"message", std::string("<") + ti.name() + "> " + e.what()}, */ // 泄露源码信息,已禁用 + {"message", "Internal Server Error"} }; - res.set(http::field::content_type, "text/plain"); + res.set(http::field::content_type, "application/json"); res.result(http::status::internal_server_error); res.body() = json::serialize(jv_r); res.prepare_payload(); -- 2.54.0 From 6f44d9497bb89b1aaba1339a785afaad8ace6aa3 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 10:10:57 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dspecial=5Fheaders?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=EF=BC=8C=E4=BF=AE=E5=A4=8DXFF=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0is=5Fsafe=5Fpath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/src/main.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/gateway/src/main.cpp b/gateway/src/main.cpp index a28558a..8ab814c 100644 --- a/gateway/src/main.cpp +++ b/gateway/src/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -277,6 +278,9 @@ http::response handleCaptcha( /* headers can't processed */ bool is_special_header(const std::string &header); +/* 请求路径安全性校验(防路径遍历/协议注入/空字节) */ +bool is_safe_target(const std::string& target); + /* configure items */ static std::string _ADDR; static int _PORT; @@ -875,9 +879,17 @@ http::response handleProxy( flag = true; } + // 路径规范化校验(P1-1):防止路径遍历、协议注入、空字节等 + // 校验在 url 拼接前完成,避免恶意 target 污染下游请求 + std::string target_path = std::string(req.target()); + if (!is_safe_target(target_path)) { + THROW_EX_CUSTOM(HttpException, 2300, "Invalid request path", + 400, "*"); + } + url = t_url; url += "/"; - url += req.target(); + url += target_path; it = req.base().find("Authorization"); if (it == req.base().end() && !flag) { @@ -945,6 +957,18 @@ http::response handleProxy( client.set_header(h); } + // 主动设置可信的 X-Forwarded-For(P1-1) + // 说明:is_special_header 已剥离客户端可能伪造的 XFF/X-Real-IP/Forwarded + // WAF → Gateway 为可信内网链路,收到的 XFF 即客户端真实 IP + // 下游服务应信任此值做限流/审计,不再被客户端伪造 + auto xff_it = req.base().find("X-Forwarded-For"); + if (xff_it != req.base().end()) { + client.set_header(std::string("X-Forwarded-For: ") + std::string(xff_it->value())); + } else { + // WAF 未传 XFF 时,标记为 unknown,避免下游拿到空值 + client.set_header("X-Forwarded-For: unknown"); + } + if (method == "get") client.get_stream_sync(url, callback); @@ -985,6 +1009,42 @@ http::response handleProxy( bool is_special_header(const std::string &header) { + // 原过滤:传输层/编码相关,由 HttpClient 自行处理 + // 新增过滤(P1-1):客户端可伪造的 IP 相关头 + // - X-Forwarded-For / X-Real-IP / Forwarded + // 剥离后由 gateway 主动设置可信值(见 handleProxy 转发逻辑) + // 防止客户端伪造 IP 绕过下游限流/审计 return header == "Host" || header == "connection" - || header == "Accept-Encoding"; + || header == "Accept-Encoding" + || header == "X-Forwarded-For" + || header == "X-Real-IP" + || header == "Forwarded"; +} + +bool is_safe_target(const std::string& target) { + // 请求路径安全性校验(P1-1) + // 防御目标:路径遍历、协议注入、空字节、非绝对路径形式 + + if (target.empty()) return false; + + // 必须以 '/' 开头(拒绝 "http://evil.com/..." 等绝对 URI 形式) + if (target[0] != '/') return false; + + // 拒绝空字节(防止截断绕过) + if (target.find('\0') != std::string::npos) return false; + + // 拒绝路径遍历:明文 ".." + if (target.find("..") != std::string::npos) return false; + + // 拒绝路径遍历:URL 编码形式(%2e / %2E,含 %2f / %5c 的编码变体) + std::string lower_target; + lower_target.reserve(target.size()); + for (char c : target) { + lower_target.push_back(static_cast(std::tolower(static_cast(c)))); + } + if (lower_target.find("%2e") != std::string::npos) return false; + if (lower_target.find("%2f") != std::string::npos) return false; + if (lower_target.find("%5c") != std::string::npos) return false; + + return true; } -- 2.54.0 From 7927d87c0a6c983ceadac8e67949a5e314a05708 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 10:20:21 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E5=9C=A8=E5=BE=AA=E7=8E=AF=E5=86=85?= =?UTF-8?q?=E5=85=88=E5=B0=86=20char=20c=20=E8=BD=AC=E4=B8=BA=20unsigned?= =?UTF-8?q?=20char=20uc=EF=BC=8C=E5=86=8D=E4=BC=A0=E5=85=A5=20std::isupper?= =?UTF-8?q?=20/=20std::islower=20/=20std::isdigit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/src/main.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gateway/src/main.cpp b/gateway/src/main.cpp index 8ab814c..8f1a462 100644 --- a/gateway/src/main.cpp +++ b/gateway/src/main.cpp @@ -152,9 +152,12 @@ public: bool has_upper = false, has_lower = false, has_digit = false, has_special = false; for (char c : password) { - if (std::isupper(c)) has_upper = true; - else if (std::islower(c)) has_lower = true; - else if (std::isdigit(c)) has_digit = true; + // std::isupper 等函数参数为 int,char 为有符号且非 ASCII 时是 UB + // 必须 static_cast 后再传入(P1-2) + unsigned char uc = static_cast(c); + if (std::isupper(uc)) has_upper = true; + else if (std::islower(uc)) has_lower = true; + else if (std::isdigit(uc)) has_digit = true; else has_special = true; } -- 2.54.0 From a9e50bb598f5436cc59899ab2ab1f95351157bd8 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 10:21:51 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E4=BF=AE=20proxy.go=20=E9=87=8C=20Director?= =?UTF-8?q?=20=E7=9A=84=20X-Forwarded-For=20=E5=B8=A6=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E5=8F=B7=E9=97=AE=E9=A2=98=E3=80=82ServeHTTP=20=E9=87=8C?= =?UTF-8?q?=E5=B7=B2=E7=BB=8F=E6=9C=89=E5=89=A5=E7=A6=BB=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8CDirector=20=E9=87=8C?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=E5=90=8C=E6=A0=B7=E7=9A=84=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=B0=B1=E8=A1=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- waf/internal/proxy/proxy.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/waf/internal/proxy/proxy.go b/waf/internal/proxy/proxy.go index 66e549d..30f5cff 100644 --- a/waf/internal/proxy/proxy.go +++ b/waf/internal/proxy/proxy.go @@ -35,7 +35,13 @@ func New(upstream string, waf *engine.Engine) (*Proxy, error) { origDirector := rp.Director rp.Director = func(req *http.Request) { origDirector(req) - req.Header.Set("X-Forwarded-For", req.RemoteAddr) + // RemoteAddr 形如 "1.2.3.4:54321",剥离端口只保留 IP, + // 否则 gateway RateLimiter 会把同 IP 不同端口的连接当成不同客户端 + clientIP := req.RemoteAddr + if idx := strings.LastIndexByte(req.RemoteAddr, ':'); idx != -1 { + clientIP = req.RemoteAddr[:idx] + } + req.Header.Set("X-Forwarded-For", clientIP) req.Header.Set("X-Forwarded-Proto", "https") } // 透传错误,不暴露内部堆栈 -- 2.54.0 From 3d8cf5250316003f67a27753b6002d9e3f1e7e84 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 10:22:58 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95=E5=9F=9F?= =?UTF-8?q?=E5=90=8D=EF=BC=9A=E7=A1=AC=E7=BC=96=E7=A0=81=20https://qs***.m?= =?UTF-8?q?e=20=E3=80=81https://www.qs***.me=20=EF=BC=8C=E5=8A=A0=E4=B8=A4?= =?UTF-8?q?=E4=B8=AA=E6=9C=AC=E5=9C=B0=E5=BC=80=E5=8F=91=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=EF=BC=88127.0.0.1:8443=E3=80=81localhost:8443=EF=BC=89?= =?UTF-8?q?=E3=80=82=20=E5=8A=A8=E6=80=81=E5=9B=9E=E5=A1=AB=20Origin?= =?UTF-8?q?=EF=BC=9A=E4=BB=85=E5=BD=93=E8=AF=B7=E6=B1=82=20Origin=20?= =?UTF-8?q?=E5=91=BD=E4=B8=AD=E7=99=BD=E5=90=8D=E5=8D=95=E6=97=B6=E6=89=8D?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=20Access-Control-Allow-Origin=EF=BC=8C?= =?UTF-8?q?=E5=90=A6=E5=88=99=E4=B8=8D=E8=BF=94=E5=9B=9E=E8=AF=A5=E5=A4=B4?= =?UTF-8?q?=EF=BC=88=E6=B5=8F=E8=A7=88=E5=99=A8=E4=BC=9A=E6=8B=92=E7=BB=9D?= =?UTF-8?q?=E8=B7=A8=E5=9F=9F=E8=AF=B7=E6=B1=82=EF=BC=89=E3=80=82=20?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20Vary:=20Origin=EF=BC=9A=E5=91=8A=E8=AF=89?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E8=AF=A5=E5=93=8D=E5=BA=94=E5=9B=A0=20Origin?= =?UTF-8?q?=20=E8=80=8C=E5=BC=82=EF=BC=8C=E9=81=BF=E5=85=8D=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E8=AF=AF=E7=94=A8=E3=80=82=20=E7=A7=BB=E9=99=A4=20Acc?= =?UTF-8?q?ess-Control-Allow-Credentials:=20true=EF=BC=9A=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E7=94=A8=20Bearer=20token=EF=BC=88=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=A4=B4=EF=BC=89=EF=BC=8C=E9=9D=9E=20cookie=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E6=AD=A4=E5=A4=B4=EF=BC=9B=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E5=8F=8D=E8=80=8C=E5=A2=9E=E5=8A=A0=E9=A3=8E=E9=99=A9=E9=9D=A2?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gateway/src/main.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/gateway/src/main.cpp b/gateway/src/main.cpp index 8f1a462..9a0ae2d 100644 --- a/gateway/src/main.cpp +++ b/gateway/src/main.cpp @@ -506,7 +506,28 @@ void registerRoutes(HttpServer &server) { server.options("*", [](const auto& req, const auto& params) { pmc::net::http::response res; res.result(pmc::net::http::status::ok); - res.set(pmc::net::http::field::access_control_allow_origin, "*"); + + // CORS 白名单(P1-4):仅允许官方域名,防止任意网站跨域调用 + // 原配置 Access-Control-Allow-Origin: * 过宽,XSS 拿到 token 后可跨域读取数据 + // 动态回填 Origin:仅当请求 Origin 命中白名单时才返回该值,否则不返回 ACAO 头 + static const std::vector allowed_origins = { + "https://qsont.me", + "https://www.qsont.me", + "http://127.0.0.1:8443", // 本地开发调试 + "http://localhost:8443" + }; + auto origin_it = req.base().find("Origin"); + if (origin_it != req.base().end()) { + std::string origin = std::string(origin_it->value()); + for (const auto& allowed : allowed_origins) { + if (origin == allowed) { + res.set(pmc::net::http::field::access_control_allow_origin, origin); + res.set(pmc::net::http::field::vary, "Origin"); + break; + } + } + } + res.set(pmc::net::http::field::access_control_allow_methods, "GET, POST, OPTIONS"); res.set(pmc::net::http::field::access_control_allow_headers, "Content-Type, Authorization"); res.set(pmc::net::http::field::access_control_max_age, "86400"); -- 2.54.0 From c028e46d0002f2ae0f0f7bb4bb3690b0210b67fa Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 25 Jul 2026 11:15:42 +0800 Subject: [PATCH 8/8] fixbug --- ai_review/agent/review_agent.py | 12 ++++++------ captcha/_run.sh | 2 +- dnfs/dnfs.py | 34 ++++++++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/ai_review/agent/review_agent.py b/ai_review/agent/review_agent.py index b83b21a..0333854 100644 --- a/ai_review/agent/review_agent.py +++ b/ai_review/agent/review_agent.py @@ -20,8 +20,8 @@ SYSTEM_PROMPT = """You are a strict content moderation AI. Your task is to revie ## Output format: Return ONLY a valid JSON object, no other text. -For violation: {{"invalid": "true"}} -For safe content: {{"invalid": "false"}} +For violation: {{"invalid": true}} +For safe content: {{"invalid": false}} ## Reference examples: {examples} @@ -42,7 +42,7 @@ class ReviewAgent: base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"), ) self.rag = RAGStore() - self.model = os.getenv("DEEPSEEK_MODEL", "deepseek-chat") + self.model = os.getenv("DEEPSEEK_MODEL", "deepseek-v4-flash") self.temperature = float(os.getenv("DEEPSEEK_TEMPERATURE", "0.0")) self.max_tokens = int(os.getenv("DEEPSEEK_MAX_TOKENS", "128")) @@ -68,7 +68,7 @@ class ReviewAgent: ) raw = response.choices[0].message.content.strip() except Exception as e: - return {"invalid": "false", "error": "api_error", "detail": str(e)} + return {"invalid": "true", "error": "api_error", "detail": str(e)} try: result = json.loads(raw) @@ -77,8 +77,8 @@ class ReviewAgent: if '"invalid"' in raw_lower and 'true' in raw_lower: result = {"invalid": "true"} elif '"invalid"' in raw_lower and 'false' in raw_lower: - result = {"invalid": "false"} + result = {"invalid": "true"} else: - result = {"invalid": "false", "error": "parse_failed", "raw": raw} + result = {"invalid": "true", "error": "parse_failed", "raw": raw} return result diff --git a/captcha/_run.sh b/captcha/_run.sh index 574e884..b4c1664 100755 --- a/captcha/_run.sh +++ b/captcha/_run.sh @@ -1,3 +1,3 @@ #!/bin/bash cd "$(dirname "$0")" || exit -python3 captcha_service.py +exec python3 captcha_service.py diff --git a/dnfs/dnfs.py b/dnfs/dnfs.py index 8772eb6..fdae19f 100755 --- a/dnfs/dnfs.py +++ b/dnfs/dnfs.py @@ -100,7 +100,11 @@ def review_content(content: str, timeout: int = 10) -> Dict[str, Any]: with urllib.request.urlopen(req, timeout=timeout) as resp: resp_data = resp.read().decode("utf-8") data = json.loads(resp_data) - return not data['invalid'] + invalid: bool = data['invalid'] + return invalid == False + #invalid = data + #return invalid + except urllib.error.HTTPError as e: # 服务端返回 4xx/5xx 错误时,尝试解析错误正文 error_body = e.read().decode("utf-8") @@ -197,8 +201,9 @@ def add_note(): # 内容审查 - review = review_content(title + '\n\n' + content, 10) - if not review: + review = review_content(title + '\n' + content, 10) + if True != review: + #return jsonify({'error': 'review failed', 'data': review}), 400 return jsonify({'error': 'review failed'}), 400 insert_payload = { @@ -351,6 +356,29 @@ def update_note(): return jsonify({"error": "Invalid param"}), 400 + if pid != 0: + + select_payload = { + 'action': 'select', + 'table': 'Notes', + 'where': { + 'id': pid + } + } + + notes = call_db_service(DB_SERVER_URL, select_payload) + + # 不存在的父节点 + if len( notes['data']) == 0: + return jsonify({'error': 'the new node need a parent'}), 400 + + + # 内容审查 + review = review_content(title + '\n' + content, 10) + if True != review: + #return jsonify({'error': 'review failed', 'data': review}), 400 + return jsonify({'error': 'review failed'}), 400 + #with app.app_context(): # note = Note.query.get(id) -- 2.54.0