From e46e60decc848029f3c1d27acbe485a0af0bcf45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 20 Jul 2025 00:59:52 +0000 Subject: [PATCH 1/5] Initial plan From b92b78ea5bcd3126a0b74d42cbfa368762b88ea4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 20 Jul 2025 01:05:07 +0000 Subject: [PATCH 2/5] Implement route restriction to only allow /sign and /nonsign endpoints Co-authored-by: SenkjM <112735335+SenkjM@users.noreply.github.com> --- openlist-proxy.js | 50 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/openlist-proxy.js b/openlist-proxy.js index 320e7f7..1a57f8a 100644 --- a/openlist-proxy.js +++ b/openlist-proxy.js @@ -165,9 +165,35 @@ function handleOptions(request) { } } +// src/handleSignToggle.js +/** + * Handles sign toggle requests to enable/disable signature verification. + * @param {Request} request - The incoming HTTP request. + * @param {boolean} enableSign - Whether to enable signature verification. + * @returns {Promise} Response indicating toggle status. + */ +async function handleSignToggle(request, enableSign) { + const origin = request.headers.get("origin") ?? "*"; + const response = new Response( + JSON.stringify({ + code: 200, + message: `Signature verification ${enableSign ? 'enabled' : 'disabled'}`, + sign_enabled: enableSign + }), + { + headers: { + "content-type": "application/json;charset=UTF-8", + "Access-Control-Allow-Origin": origin + } + } + ); + response.headers.append("Vary", "Origin"); + return response; +} + // src/handleRequest.js /** - * Main request handler that routes based on HTTP method. + * Main request handler that routes based on path. * @param {Request} request - The incoming HTTP request. * @returns {Promise} A valid response. */ @@ -175,7 +201,27 @@ async function handleRequest(request) { if (request.method === "OPTIONS") { return handleOptions(request); } - return await handleDownload(request); + + const url = new URL(request.url); + const pathname = url.pathname; + + // Only allow /sign and /nonsign endpoints + if (pathname === "/sign") { + return await handleSignToggle(request, true); + } else if (pathname === "/nonsign") { + return await handleSignToggle(request, false); + } else { + // All other paths return 403 Forbidden + const origin = request.headers.get("origin") ?? "*"; + const response = new Response("Forbidden", { + status: 403, + headers: { + "Access-Control-Allow-Origin": origin + } + }); + response.headers.append("Vary", "Origin"); + return response; + } } // src/index.js From 5cbdbfdd610479ed61b1d75829d1d4ee36df9657 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 20 Jul 2025 01:17:52 +0000 Subject: [PATCH 3/5] Fix proxy logic to preserve downloads with conditional signature verification - Restore original proxy download functionality for all paths - Add conditional signature verification: /nonsign/* paths skip verification - /sign/* and other paths require signature verification as before Co-authored-by: SenkjM <112735335+SenkjM@users.noreply.github.com> --- openlist-proxy.js | 55 ++++++----------------------------------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/openlist-proxy.js b/openlist-proxy.js index 1a57f8a..6b52a60 100644 --- a/openlist-proxy.js +++ b/openlist-proxy.js @@ -71,7 +71,10 @@ async function handleDownload(request) { const url = new URL(request.url); const path = decodeURIComponent(url.pathname); const sign = url.searchParams.get("sign") ?? ""; - const verifyResult = await verify(path, sign); + + // Skip signature verification for paths starting with /nonsign/ + const shouldSkipVerification = path.startsWith("/nonsign/"); + const verifyResult = shouldSkipVerification ? "" : await verify(path, sign); if (verifyResult !== "") { const resp2 = new Response( JSON.stringify({ @@ -165,35 +168,9 @@ function handleOptions(request) { } } -// src/handleSignToggle.js -/** - * Handles sign toggle requests to enable/disable signature verification. - * @param {Request} request - The incoming HTTP request. - * @param {boolean} enableSign - Whether to enable signature verification. - * @returns {Promise} Response indicating toggle status. - */ -async function handleSignToggle(request, enableSign) { - const origin = request.headers.get("origin") ?? "*"; - const response = new Response( - JSON.stringify({ - code: 200, - message: `Signature verification ${enableSign ? 'enabled' : 'disabled'}`, - sign_enabled: enableSign - }), - { - headers: { - "content-type": "application/json;charset=UTF-8", - "Access-Control-Allow-Origin": origin - } - } - ); - response.headers.append("Vary", "Origin"); - return response; -} - // src/handleRequest.js /** - * Main request handler that routes based on path. + * Main request handler that routes based on HTTP method. * @param {Request} request - The incoming HTTP request. * @returns {Promise} A valid response. */ @@ -201,27 +178,7 @@ async function handleRequest(request) { if (request.method === "OPTIONS") { return handleOptions(request); } - - const url = new URL(request.url); - const pathname = url.pathname; - - // Only allow /sign and /nonsign endpoints - if (pathname === "/sign") { - return await handleSignToggle(request, true); - } else if (pathname === "/nonsign") { - return await handleSignToggle(request, false); - } else { - // All other paths return 403 Forbidden - const origin = request.headers.get("origin") ?? "*"; - const response = new Response("Forbidden", { - status: 403, - headers: { - "Access-Control-Allow-Origin": origin - } - }); - response.headers.append("Vary", "Origin"); - return response; - } + return await handleDownload(request); } // src/index.js From 478180164799a89486ab64056fa35f82cebecbdc Mon Sep 17 00:00:00 2001 From: SenkjM Date: Sun, 20 Jul 2025 10:08:51 +0800 Subject: [PATCH 4/5] feat(cf-work): add configurable nonsign and sign endpoints --- openlist-proxy.js | 76 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/openlist-proxy.js b/openlist-proxy.js index 6b52a60..dba45e5 100644 --- a/openlist-proxy.js +++ b/openlist-proxy.js @@ -2,6 +2,9 @@ const ADDRESS = "YOUR_ADDRESS"; const TOKEN = "YOUR_TOKEN"; const WORKER_ADDRESS = "YOUR_WORKER_ADDRESS"; +const ENABLE_NONSIGN = false; +// 设置为 true 可开启 nonsign 端点,会造成所有文件可通过路径被访问 +// Setting to true enables the nonsign endpoint, which introduces the risk of all files being accessible through their paths. // src/verify.js /** @@ -72,24 +75,75 @@ async function handleDownload(request) { const path = decodeURIComponent(url.pathname); const sign = url.searchParams.get("sign") ?? ""; - // Skip signature verification for paths starting with /nonsign/ - const shouldSkipVerification = path.startsWith("/nonsign/"); - const verifyResult = shouldSkipVerification ? "" : await verify(path, sign); - if (verifyResult !== "") { - const resp2 = new Response( + let actualPath; + let needVerifySign = false; + + // 检查端点类型 + if (path.startsWith("/sign/")) { + // /sign 端点:需要签名验证 + actualPath = path.substring(5); // 移除 "/sign" + needVerifySign = true; + } else if (path.startsWith("/nonsign/")) { + // 检查是否启用 nonsign 端点 + if (!ENABLE_NONSIGN) { + return new Response( + JSON.stringify({ + code: 404, + message: "Not found", + }), + { + headers: { + "content-type": "application/json;charset=UTF-8", + "Access-Control-Allow-Origin": origin, + }, + } + ); + } + // /nonsign 端点:不需要签名验证 + actualPath = path.substring(9); // 移除 "/nonsign" + needVerifySign = false; + } else { + // 其他路径:返回 404 + return new Response( JSON.stringify({ - code: 401, - message: verifyResult, + code: 404, + message: "Not found", }), { headers: { "content-type": "application/json;charset=UTF-8", + "Access-Control-Allow-Origin": origin, }, } ); - resp2.headers.set("Access-Control-Allow-Origin", origin); - return resp2; } + + // 如果需要签名验证,进行验证 + if (needVerifySign) { + // 尝试两种签名验证方式:先验证不包含 /sign 的路径,如果失败再验证包含 /sign 的路径 + let verifyResult = await verify(actualPath, sign); + if (verifyResult !== "") { + // 如果使用实际路径验证失败,尝试使用完整路径(包含 /sign) + verifyResult = await verify(path, sign); + if (verifyResult !== "") { + const resp2 = new Response( + JSON.stringify({ + code: 401, + message: verifyResult, + }), + { + headers: { + "content-type": "application/json;charset=UTF-8", + }, + } + ); + resp2.headers.set("Access-Control-Allow-Origin", origin); + return resp2; + } + } + } + + // 使用实际路径进行文件获取 let resp = await fetch(`${ADDRESS}/api/fs/link`, { method: "POST", headers: { @@ -97,7 +151,7 @@ async function handleDownload(request) { Authorization: TOKEN, }, body: JSON.stringify({ - path, + path: actualPath, }), }); let res = await resp.json(); @@ -194,4 +248,4 @@ var src_default = { return await handleRequest(request); }, }; -export { src_default as default }; +export { src_default as default }; \ No newline at end of file From b8ba019a7a119319744a00e894695262a6edf2bf Mon Sep 17 00:00:00 2001 From: SenkjM Date: Sun, 20 Jul 2025 10:22:07 +0800 Subject: [PATCH 5/5] feat(proxy.go): support configurable nonsign and sign endpoints based on JS logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --enable-nonsign=false/true参数,可以选择是否打开nonsign端点 go语言版本未经测试 --- openlist-proxy.go | 66 +++++++++++++++++++++++++++++++++++++++++------ openlist-proxy.js | 33 ++++++++++-------------- 2 files changed, 72 insertions(+), 27 deletions(-) diff --git a/openlist-proxy.go b/openlist-proxy.go index 675800b..5da8ac9 100644 --- a/openlist-proxy.go +++ b/openlist-proxy.go @@ -31,6 +31,7 @@ var ( showVersion bool certFile, keyFile string address, token string + enableNonsign bool s sign.Sign version string = "dev" ) @@ -44,6 +45,7 @@ func init() { flag.StringVar(&keyFile, "key", "server.key", "key file") flag.StringVar(&address, "address", "", "openlist address") flag.StringVar(&token, "token", "", "openlist token") + flag.BoolVar(&enableNonsign, "enable-nonsign", false, "enable nonsign endpoint (security risk)") flag.Parse() s = sign.NewHMACSign([]byte(token)) @@ -66,15 +68,62 @@ func errorResponse(w http.ResponseWriter, code int, msg string) { } func downHandle(w http.ResponseWriter, r *http.Request) { - sign := r.URL.Query().Get("sign") - filePath := r.URL.Path - err := s.Verify(filePath, sign) - if err != nil { - errorResponse(w, 401, err.Error()) + // 设置 CORS 头 + origin := r.Header.Get("Origin") + if origin == "" { + origin = "*" + } + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, OPTIONS") + w.Header().Add("Access-Control-Allow-Headers", "range") + w.Header().Add("Vary", "Origin") + + // 处理 OPTIONS 请求 + if r.Method == "OPTIONS" { + w.Header().Set("Access-Control-Max-Age", "86400") + if r.Header.Get("Access-Control-Request-Headers") != "" { + w.Header().Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers")) + } + w.WriteHeader(200) + return + } + + path := r.URL.Path + var actualPath string + var needVerifySign bool + + // 检查端点类型 + if strings.HasPrefix(path, "/sign/") { + // /sign 端点:需要签名验证 + actualPath = path[5:] // 移除 "/sign" + needVerifySign = true + } else if strings.HasPrefix(path, "/nonsign/") { + // 检查是否启用 nonsign 端点 + if !enableNonsign { + errorResponse(w, 404, "Not found") + return + } + // /nonsign 端点:不需要签名验证 + actualPath = path[9:] // 移除 "/nonsign" + needVerifySign = false + } else { + // 其他路径:返回 404 + errorResponse(w, 404, "Not found") return } + + // 如果需要签名验证,进行验证 + if needVerifySign { + sign := r.URL.Query().Get("sign") + err := s.Verify(actualPath, sign) + if err != nil { + errorResponse(w, 401, err.Error()) + return + } + } + data := Json{ - "path": filePath, + "path": actualPath, } dataByte, _ := json.Marshal(data) req, _ := http.NewRequest("POST", fmt.Sprintf("%s/api/fs/link", address), bytes.NewBuffer(dataByte)) @@ -125,9 +174,10 @@ func downHandle(w http.ResponseWriter, r *http.Request) { res2.Header.Del("Access-Control-Allow-Origin") res2.Header.Del("set-cookie") maps.Copy(w.Header(), res2.Header) - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, OPTIONS") w.Header().Add("Access-Control-Allow-Headers", "range") + w.Header().Add("Vary", "Origin") w.WriteHeader(res2.StatusCode) _, err = io.Copy(w, res2.Body) if err != nil { diff --git a/openlist-proxy.js b/openlist-proxy.js index dba45e5..a3aef5e 100644 --- a/openlist-proxy.js +++ b/openlist-proxy.js @@ -120,26 +120,21 @@ async function handleDownload(request) { // 如果需要签名验证,进行验证 if (needVerifySign) { - // 尝试两种签名验证方式:先验证不包含 /sign 的路径,如果失败再验证包含 /sign 的路径 - let verifyResult = await verify(actualPath, sign); + const verifyResult = await verify(actualPath, sign); if (verifyResult !== "") { - // 如果使用实际路径验证失败,尝试使用完整路径(包含 /sign) - verifyResult = await verify(path, sign); - if (verifyResult !== "") { - const resp2 = new Response( - JSON.stringify({ - code: 401, - message: verifyResult, - }), - { - headers: { - "content-type": "application/json;charset=UTF-8", - }, - } - ); - resp2.headers.set("Access-Control-Allow-Origin", origin); - return resp2; - } + const resp2 = new Response( + JSON.stringify({ + code: 401, + message: verifyResult, + }), + { + headers: { + "content-type": "application/json;charset=UTF-8", + }, + } + ); + resp2.headers.set("Access-Control-Allow-Origin", origin); + return resp2; } }