mirror of
https://github.com/teamchong/pxpipe.git
synced 2026-07-22 02:02:51 +02:00
abc0194219
Prevent Anthropic bearer tokens from being forwarded to OpenAI when routing /v1/models. Add tests for Anthropic, OpenAI, and x-api-key routing.
68 lines
2.4 KiB
TypeScript
68 lines
2.4 KiB
TypeScript
/**
|
|
* /v1/models routing by auth style. The path exists on BOTH APIs, so the
|
|
* proxy sniffs the auth header — but an `sk-ant-…` bearer is Anthropic by
|
|
* construction (Claude Code subscription auth sends
|
|
* `authorization: Bearer sk-ant-oat01-…` with no x-api-key). It must never
|
|
* be forwarded to the OpenAI upstream. All tokens here are fake; the suite
|
|
* never touches the network (global fetch is stubbed).
|
|
*/
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
import { createProxy } from '../src/core/proxy.js';
|
|
|
|
const realFetch = globalThis.fetch;
|
|
afterEach(() => {
|
|
globalThis.fetch = realFetch;
|
|
});
|
|
|
|
function stubFetch(capture: { url?: string; headers?: Headers }) {
|
|
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
capture.url = String(input);
|
|
capture.headers = new Headers(init?.headers);
|
|
return new Response(JSON.stringify({ data: [] }), {
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
}) as typeof fetch;
|
|
}
|
|
|
|
describe('/v1/models auth-style routing', () => {
|
|
const proxy = () => createProxy({});
|
|
|
|
it('routes an sk-ant bearer (Claude Code OAuth) to the Anthropic upstream', async () => {
|
|
const cap: { url?: string; headers?: Headers } = {};
|
|
stubFetch(cap);
|
|
await proxy()(
|
|
new Request('http://localhost/v1/models', {
|
|
method: 'GET',
|
|
headers: { authorization: 'Bearer sk-ant-oat01-fake-oauth-token' },
|
|
}),
|
|
);
|
|
expect(cap.url).toBe('https://api.anthropic.com/v1/models');
|
|
// The token stays on the Anthropic leg, never crosses to api.openai.com.
|
|
expect(cap.headers?.get('authorization')).toBe('Bearer sk-ant-oat01-fake-oauth-token');
|
|
});
|
|
|
|
it('still routes a non-Anthropic bearer to the OpenAI upstream', async () => {
|
|
const cap: { url?: string; headers?: Headers } = {};
|
|
stubFetch(cap);
|
|
await proxy()(
|
|
new Request('http://localhost/v1/models', {
|
|
method: 'GET',
|
|
headers: { authorization: 'Bearer fake-openai-key' },
|
|
}),
|
|
);
|
|
expect(cap.url).toBe('https://api.openai.com/v1/models');
|
|
});
|
|
|
|
it('still routes x-api-key requests to the Anthropic upstream', async () => {
|
|
const cap: { url?: string; headers?: Headers } = {};
|
|
stubFetch(cap);
|
|
await proxy()(
|
|
new Request('http://localhost/v1/models', {
|
|
method: 'GET',
|
|
headers: { 'x-api-key': 'fake-anthropic-key' },
|
|
}),
|
|
);
|
|
expect(cap.url).toBe('https://api.anthropic.com/v1/models');
|
|
});
|
|
});
|