mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 15:22:12 +03:00
fix(claude): apply tool cloak + schema sanitize on the CLIProxyAPI executor path
The native Claude OAuth guard in executors/base.ts is bypassed when `upstream_proxy_config.mode = cliproxyapi` routes the request through the CliproxyAPI executor — it has its own execute()/transformRequest() and never reaches BaseExecutor.execute(), so the cloak/sanitizer never ran for that (common) deployment. Wire the same guards into CliproxyapiExecutor.transformRequest (Anthropic-shape branch), composing with the existing bisected `mcp_*` reserved-namespace rewrite: - sanitizeClaudeToolSchemas() on transformed.tools. - cloakThirdPartyToolNames() with skip = mcp-reserved, so applyMcpToolNameRewrite keeps authority over `mcp_*` (its bisected `Mcp_X` form) and the two reverse maps stay disjoint / single-hop. Both merge into the non-enumerable _toolNameMap the response stream already uses to restore the caller's names. cloakThirdPartyToolNames is now non-mutating (clones changed entries) to respect transformRequest's no-input-mutation contract, and takes an optional `skip` predicate. Verified end-to-end through the live CPA path: a real ~100-tool harness payload that returned the "out of extra usage" placeholder now returns 200 with original tool names restored on the response stream; `mcp_*` tools and genuine PascalCase Claude Code tools are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -179,3 +179,31 @@ describe("cloakThirdPartyToolNames — defensive null guards", () => {
|
||||
assert.equal(block.name, "Read");
|
||||
});
|
||||
});
|
||||
|
||||
describe("cloakThirdPartyToolNames — non-mutating + skip option", () => {
|
||||
it("does not mutate the caller's input tool objects", () => {
|
||||
const original: AnyRecord = { name: "read_file" };
|
||||
const body: AnyRecord = { tools: [original] };
|
||||
cloakThirdPartyToolNames(body);
|
||||
assert.equal(original.name, "read_file"); // input object untouched
|
||||
assert.equal((body.tools as AnyRecord[])[0].name, "Read"); // body.tools reassigned with a clone
|
||||
});
|
||||
|
||||
it("does not mutate the caller's input message blocks", () => {
|
||||
const block: AnyRecord = { type: "tool_use", name: "read_file" };
|
||||
const body: AnyRecord = {
|
||||
tools: [{ name: "read_file" }],
|
||||
messages: [{ role: "assistant", content: [block] }],
|
||||
};
|
||||
cloakThirdPartyToolNames(body);
|
||||
assert.equal(block.name, "read_file"); // input block untouched
|
||||
const out = ((body.messages as AnyRecord[])[0].content as AnyRecord[])[0];
|
||||
assert.equal(out.name, "Read");
|
||||
});
|
||||
|
||||
it("leaves names matched by the skip predicate untouched", () => {
|
||||
const body: AnyRecord = { tools: [{ name: "mcp_call" }, { name: "read_file" }] };
|
||||
cloakThirdPartyToolNames(body, { skip: (n) => n.startsWith("mcp_") });
|
||||
assert.deepEqual((body.tools as AnyRecord[]).map((t) => t.name), ["mcp_call", "Read"]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user