fix: proxy project management write resources (#2167)

This commit is contained in:
Lyon
2026-06-25 23:32:14 +08:00
committed by GitHub
parent 56033bce93
commit 1a6dd66a97
2 changed files with 48 additions and 4 deletions
@@ -0,0 +1,37 @@
import assert from "node:assert/strict";
import { test } from "bun:test";
import { isAllowedProjectManagementProxyMethod } from "./project-management-proxy.ts";
test("project management proxy forwards public MDTODO write resources", () => {
const allowed = [
["GET", "/v1/project-management/mdtodo/sources"],
["HEAD", "/v1/project-management/mdtodo/tasks"],
["POST", "/v1/project-management/workbench-links"],
["POST", "/v1/project-management/mdtodo/sources"],
["PATCH", "/v1/project-management/mdtodo/sources/d601-f103-v2-mdtodo"],
["DELETE", "/v1/project-management/mdtodo/sources/d601-f103-v2-mdtodo"],
["POST", "/v1/project-management/mdtodo/sources/d601-f103-v2-mdtodo/probe"],
["POST", "/v1/project-management/mdtodo/sources/d601-f103-v2-mdtodo/reindex"],
["PATCH", "/v1/project-management/mdtodo/files/file_demo/content"],
["POST", "/v1/project-management/mdtodo/tasks"],
["PATCH", "/v1/project-management/mdtodo/tasks/mdtodo%3Asource%3Afile%3AR1.1"],
["DELETE", "/v1/project-management/mdtodo/tasks/mdtodo%3Asource%3Afile%3AR1.1"]
];
for (const [method, pathname] of allowed) {
assert.equal(isAllowedProjectManagementProxyMethod(method, pathname), true, `${method} ${pathname}`);
}
});
test("project management proxy keeps non-contract writes blocked", () => {
const blocked = [
["PUT", "/v1/project-management/mdtodo/sources/d601-f103-v2-mdtodo"],
["POST", "/v1/project-management/mdtodo/sources/d601-f103-v2-mdtodo/delete"],
["POST", "/v1/project-management/mdtodo/files/file_demo/content"],
["PATCH", "/v1/project-management/mdtodo/tasks"],
["POST", "/v1/project-management/internal/rebuild"]
];
for (const [method, pathname] of blocked) {
assert.equal(isAllowedProjectManagementProxyMethod(method, pathname), false, `${method} ${pathname}`);
}
});
+11 -4
View File
@@ -17,8 +17,8 @@ export async function handleProjectManagementProxyHttp(request, response, url, o
return;
}
if (!isAllowedProjectManagementMethod(request.method, url.pathname)) {
sendJson(response, 405, { ok: false, error: { code: "method_not_allowed", message: "Project management API only accepts read requests plus Workbench link writes" } });
if (!isAllowedProjectManagementProxyMethod(request.method, url.pathname)) {
sendJson(response, 405, { ok: false, error: { code: "method_not_allowed", message: "Project management API method/path is not part of the public Project Management proxy contract" } });
return;
}
@@ -69,9 +69,16 @@ export async function handleProjectManagementProxyHttp(request, response, url, o
}
}
function isAllowedProjectManagementMethod(method, pathname) {
export function isAllowedProjectManagementProxyMethod(method, pathname) {
if (method === "GET" || method === "HEAD") return true;
return method === "POST" && pathname === "/v1/project-management/workbench-links";
if (method === "POST" && pathname === "/v1/project-management/workbench-links") return true;
if (method === "POST" && pathname === "/v1/project-management/mdtodo/sources") return true;
if ((method === "PATCH" || method === "DELETE") && /^\/v1\/project-management\/mdtodo\/sources\/[^/]+$/u.test(pathname)) return true;
if (method === "POST" && /^\/v1\/project-management\/mdtodo\/sources\/[^/]+\/(?:probe|reindex)$/u.test(pathname)) return true;
if (method === "PATCH" && /^\/v1\/project-management\/mdtodo\/files\/[^/]+\/content$/u.test(pathname)) return true;
if (method === "POST" && pathname === "/v1/project-management/mdtodo/tasks") return true;
if ((method === "PATCH" || method === "DELETE") && /^\/v1\/project-management\/mdtodo\/tasks\/[^/]+$/u.test(pathname)) return true;
return false;
}
function bridgeHeaders(request, auth) {