mirror of
https://github.com/musistudio/claude-code-router.git
synced 2026-08-29 03:12:10 +08:00
Allow authenticated RPC requests from any origin
This commit is contained in:
@@ -205,10 +205,6 @@ async function handleRpcRequest(request: IncomingMessage, response: ServerRespon
|
||||
sendJson(response, 415, { error: { message: "RPC requests must use application/json." }, ok: false });
|
||||
return;
|
||||
}
|
||||
if (!isAllowedWebRequestOrigin(request, security)) {
|
||||
sendJson(response, 403, { error: { message: "Forbidden RPC origin." }, ok: false });
|
||||
return;
|
||||
}
|
||||
if (!hasValidWebAuthToken(request, security)) {
|
||||
sendJson(response, 401, { error: { message: "CCR web authentication token is missing or invalid." }, ok: false });
|
||||
return;
|
||||
@@ -602,32 +598,6 @@ function isAllowedWebRequestHost(request: IncomingMessage, security: WebManageme
|
||||
return Boolean(hostname && isAllowedWebHostname(hostname, security));
|
||||
}
|
||||
|
||||
function isAllowedWebRequestOrigin(request: IncomingMessage, security: WebManagementSecurityContext): boolean {
|
||||
const origin = readHeaderValue(request.headers.origin);
|
||||
if (origin && !isAllowedWebOriginValue(origin, security)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const referer = readHeaderValue(request.headers.referer);
|
||||
if (!origin && referer && !isAllowedWebOriginValue(referer, security)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isAllowedWebOriginValue(value: string, security: WebManagementSecurityContext): boolean {
|
||||
try {
|
||||
const url = new URL(value);
|
||||
const port = url.port ? Number(url.port) : url.protocol === "http:" ? 80 : url.protocol === "https:" ? 443 : undefined;
|
||||
return url.protocol === "http:" &&
|
||||
port === security.port &&
|
||||
isAllowedWebHostname(normalizeHostname(url.hostname), security);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isAllowedWebHostname(hostname: string, security: WebManagementSecurityContext): boolean {
|
||||
const normalized = normalizeHostname(hostname);
|
||||
return security.allowedHostnames.has(normalized) ||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { normalizeExternalHttpTarget } from "@ccr/core/web/management-server.ts";
|
||||
import { normalizeExternalHttpTarget, startWebManagementServer } from "@ccr/core/web/management-server.ts";
|
||||
|
||||
test("normalizeExternalHttpTarget accepts absolute http, https, and CCR plugin URLs only", () => {
|
||||
assert.equal(normalizeExternalHttpTarget(""), undefined);
|
||||
@@ -12,3 +12,33 @@ test("normalizeExternalHttpTarget accepts absolute http, https, and CCR plugin U
|
||||
assert.throws(() => normalizeExternalHttpTarget("javascript:alert(1)"), /Only http, https, and CCR plugin URLs/);
|
||||
assert.throws(() => normalizeExternalHttpTarget("example.com"), /valid absolute URL/);
|
||||
});
|
||||
|
||||
test("web RPC ignores Origin and Referer when the auth token is valid", async () => {
|
||||
const authToken = "test-web-auth-token";
|
||||
const runtime = await startWebManagementServer({
|
||||
authToken,
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
startGateway: false
|
||||
});
|
||||
try {
|
||||
const endpoint = new URL("/api/ccr/rpc", runtime.url);
|
||||
const response = await fetch(endpoint, {
|
||||
body: JSON.stringify({ args: [], method: "getAppInfo" }),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"origin": "http://127.0.0.1:8000",
|
||||
"referer": "http://127.0.0.1:8000/",
|
||||
"x-ccr-web-auth": authToken
|
||||
},
|
||||
method: "POST"
|
||||
});
|
||||
const payload = await response.json();
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal(payload.ok, true);
|
||||
assert.equal(payload.value.name, "Claude Code Router");
|
||||
} finally {
|
||||
await runtime.close();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user