forked from boos/BoosAPI
default deny empty downstream key exposure
This commit is contained in:
@@ -136,6 +136,17 @@ describe('downstreamApiKeyService', () => {
|
||||
expect(await service.isModelAllowedByPolicyOrAllowedRoutes('gemini-2.0-flash', policy)).toBe(false);
|
||||
});
|
||||
|
||||
it('denies all models when both supportedModels and allowedRouteIds are empty', async () => {
|
||||
const policy = {
|
||||
supportedModels: [],
|
||||
allowedRouteIds: [],
|
||||
siteWeightMultipliers: {},
|
||||
};
|
||||
|
||||
expect(await service.isModelAllowedByPolicyOrAllowedRoutes('gpt-4o-mini', policy)).toBe(false);
|
||||
expect(await service.isModelAllowedByPolicyOrAllowedRoutes('claude-opus-4-6', policy)).toBe(false);
|
||||
});
|
||||
|
||||
it('authorizes by selected group model pattern only, not arbitrary internal models', async () => {
|
||||
const virtualModelGroup = await db.insert(schema.tokenRoutes).values({
|
||||
modelPattern: 'claude-opus-4-6',
|
||||
|
||||
@@ -234,7 +234,7 @@ export async function isModelAllowedByPolicyOrAllowedRoutes(model: string, polic
|
||||
const hasPatternRules = patterns.length > 0;
|
||||
const hasRouteRules = allowedRouteIds.length > 0;
|
||||
|
||||
if (!hasPatternRules && !hasRouteRules) return true;
|
||||
if (!hasPatternRules && !hasRouteRules) return false;
|
||||
|
||||
if (hasPatternRules && patterns.some((pattern) => matchesDownstreamModelPattern(model, pattern))) {
|
||||
return true;
|
||||
|
||||
@@ -9,3 +9,9 @@ export const EMPTY_DOWNSTREAM_ROUTING_POLICY: DownstreamRoutingPolicy = {
|
||||
allowedRouteIds: [],
|
||||
siteWeightMultipliers: {},
|
||||
};
|
||||
|
||||
export const GLOBAL_UNRESTRICTED_DOWNSTREAM_ROUTING_POLICY: DownstreamRoutingPolicy = {
|
||||
supportedModels: ['*'],
|
||||
allowedRouteIds: [],
|
||||
siteWeightMultipliers: {},
|
||||
};
|
||||
|
||||
@@ -102,6 +102,46 @@ describe('TokenRouter downstream policy', () => {
|
||||
expect(blockedPick).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects route selection when both supportedModels and allowedRouteIds are empty', async () => {
|
||||
const site = await db.insert(schema.sites).values({
|
||||
name: 'site-deny-all',
|
||||
url: 'https://deny-all.example.com',
|
||||
platform: 'new-api',
|
||||
status: 'active',
|
||||
}).returning().get();
|
||||
|
||||
const account = await db.insert(schema.accounts).values({
|
||||
siteId: site.id,
|
||||
username: 'user-deny-all',
|
||||
accessToken: 'access-deny-all',
|
||||
apiToken: 'sk-deny-all',
|
||||
status: 'active',
|
||||
}).returning().get();
|
||||
|
||||
const route = await db.insert(schema.tokenRoutes).values({
|
||||
modelPattern: 'gpt-4o-mini',
|
||||
enabled: true,
|
||||
}).returning().get();
|
||||
|
||||
await db.insert(schema.routeChannels).values({
|
||||
routeId: route.id,
|
||||
accountId: account.id,
|
||||
tokenId: null,
|
||||
priority: 0,
|
||||
weight: 10,
|
||||
enabled: true,
|
||||
}).run();
|
||||
|
||||
const router = new TokenRouter();
|
||||
const deniedPick = await router.selectChannel('gpt-4o-mini', {
|
||||
allowedRouteIds: [],
|
||||
supportedModels: [],
|
||||
siteWeightMultipliers: {},
|
||||
});
|
||||
|
||||
expect(deniedPick).toBeNull();
|
||||
});
|
||||
|
||||
it('applies site weight multipliers to probability explanation', async () => {
|
||||
const siteHigh = await db.insert(schema.sites).values({
|
||||
name: 'high-site',
|
||||
|
||||
@@ -531,15 +531,18 @@ function channelSupportsRequestedModel(channelSourceModel: string | null | undef
|
||||
return false;
|
||||
}
|
||||
|
||||
function isModelAllowedByDownstreamPolicy(requestedModel: string, policy: DownstreamRoutingPolicy): boolean {
|
||||
const supportedPatterns = Array.isArray(policy.supportedModels)
|
||||
? policy.supportedModels
|
||||
: [];
|
||||
const matchedSupportedPattern = supportedPatterns.some((pattern) => matchesModelPattern(requestedModel, pattern));
|
||||
if (matchedSupportedPattern) return true;
|
||||
if (policy.allowedRouteIds.length > 0) return true;
|
||||
return supportedPatterns.length === 0;
|
||||
}
|
||||
function isModelAllowedByDownstreamPolicy(requestedModel: string, policy: DownstreamRoutingPolicy): boolean {
|
||||
const supportedPatterns = Array.isArray(policy.supportedModels)
|
||||
? policy.supportedModels
|
||||
: [];
|
||||
const hasSupportedPatterns = supportedPatterns.length > 0;
|
||||
const hasAllowedRoutes = policy.allowedRouteIds.length > 0;
|
||||
if (!hasSupportedPatterns && !hasAllowedRoutes) return false;
|
||||
const matchedSupportedPattern = supportedPatterns.some((pattern) => matchesModelPattern(requestedModel, pattern));
|
||||
if (matchedSupportedPattern) return true;
|
||||
if (hasAllowedRoutes) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function resolveMappedModel(requestedModel: string, modelMapping?: string | null): string {
|
||||
if (!modelMapping) return requestedModel;
|
||||
|
||||
Reference in New Issue
Block a user