Files
2026-07-13 13:20:22 +08:00

156 lines
5.3 KiB
TypeScript

/**
* @license
* Copyright 2025 AionUi (aionui.com)
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import {
isOfficialHost,
isGoogleApisHost,
isOpenAIHost,
isAnthropicHost,
API_HOST_CONFIG,
GOOGLE_API_HOSTS,
} from '@/common/utils/urlValidation';
describe('urlValidation', () => {
describe('isOfficialHost', () => {
const allowedHosts = ['api.example.com', 'api.test.com'];
it('returns true for allowed host', () => {
expect(isOfficialHost('https://api.example.com/v1', allowedHosts)).toBe(true);
expect(isOfficialHost('https://api.test.com/resource', allowedHosts)).toBe(true);
});
it('returns false for host not in allowed list', () => {
expect(isOfficialHost('https://evil.com/v1', allowedHosts)).toBe(false);
expect(isOfficialHost('https://api.other.com/v1', allowedHosts)).toBe(false);
});
it('returns false for subdomain attack', () => {
expect(isOfficialHost('https://api.example.com.evil.com/v1', allowedHosts)).toBe(false);
});
it('returns false for path-based attack', () => {
expect(isOfficialHost('https://evil.com/api.example.com', allowedHosts)).toBe(false);
});
it('returns false for invalid URL', () => {
expect(isOfficialHost('not a url', allowedHosts)).toBe(false);
expect(isOfficialHost('', allowedHosts)).toBe(false);
});
it('handles URL with port', () => {
expect(isOfficialHost('https://api.example.com:8080/v1', allowedHosts)).toBe(true);
});
it('handles URL with query parameters', () => {
expect(isOfficialHost('https://api.example.com/v1?key=value', allowedHosts)).toBe(true);
});
it('handles URL with fragments', () => {
expect(isOfficialHost('https://api.example.com/v1#section', allowedHosts)).toBe(true);
});
});
describe('isGoogleApisHost', () => {
it('returns true for Gemini API host', () => {
expect(isGoogleApisHost('https://generativelanguage.googleapis.com/v1')).toBe(true);
expect(isGoogleApisHost('https://generativelanguage.googleapis.com/v1/models')).toBe(true);
});
it('returns true for Vertex AI host', () => {
expect(isGoogleApisHost('https://aiplatform.googleapis.com/v1')).toBe(true);
});
it('returns false for non-Google host', () => {
expect(isGoogleApisHost('https://api.openai.com/v1')).toBe(false);
expect(isGoogleApisHost('https://evil.com')).toBe(false);
});
it('returns false for subdomain attack on Google host', () => {
expect(isGoogleApisHost('https://generativelanguage.googleapis.com.evil.com/v1')).toBe(false);
});
it('returns false for path-based attack', () => {
expect(isGoogleApisHost('https://evil.com/generativelanguage.googleapis.com')).toBe(false);
});
it('returns false for invalid URL', () => {
expect(isGoogleApisHost('invalid')).toBe(false);
});
});
describe('isOpenAIHost', () => {
it('returns true for OpenAI API host', () => {
expect(isOpenAIHost('https://api.openai.com/v1')).toBe(true);
expect(isOpenAIHost('https://api.openai.com/v1/chat/completions')).toBe(true);
});
it('returns false for non-OpenAI host', () => {
expect(isOpenAIHost('https://generativelanguage.googleapis.com/v1')).toBe(false);
expect(isOpenAIHost('https://evil.com')).toBe(false);
});
it('returns false for subdomain attack', () => {
expect(isOpenAIHost('https://api.openai.com.evil.com/v1')).toBe(false);
});
it('returns false for path-based attack', () => {
expect(isOpenAIHost('https://evil.com/api.openai.com')).toBe(false);
});
it('returns false for invalid URL', () => {
expect(isOpenAIHost('not a url')).toBe(false);
});
});
describe('isAnthropicHost', () => {
it('returns true for Anthropic API host', () => {
expect(isAnthropicHost('https://api.anthropic.com/v1')).toBe(true);
expect(isAnthropicHost('https://api.anthropic.com/v1/messages')).toBe(true);
});
it('returns false for non-Anthropic host', () => {
expect(isAnthropicHost('https://api.openai.com/v1')).toBe(false);
expect(isAnthropicHost('https://evil.com')).toBe(false);
});
it('returns false for subdomain attack', () => {
expect(isAnthropicHost('https://api.anthropic.com.evil.com/v1')).toBe(false);
});
it('returns false for path-based attack', () => {
expect(isAnthropicHost('https://evil.com/api.anthropic.com')).toBe(false);
});
it('returns false for invalid URL', () => {
expect(isAnthropicHost('invalid')).toBe(false);
});
});
describe('API_HOST_CONFIG', () => {
it('exports expected Google hosts', () => {
expect(API_HOST_CONFIG.google.gemini).toBe('generativelanguage.googleapis.com');
expect(API_HOST_CONFIG.google.vertexAi).toBe('aiplatform.googleapis.com');
});
it('exports expected OpenAI host', () => {
expect(API_HOST_CONFIG.openai.api).toBe('api.openai.com');
});
it('exports expected Anthropic host', () => {
expect(API_HOST_CONFIG.anthropic.api).toBe('api.anthropic.com');
});
});
describe('GOOGLE_API_HOSTS', () => {
it('contains all Google API hosts', () => {
expect(GOOGLE_API_HOSTS).toContain('generativelanguage.googleapis.com');
expect(GOOGLE_API_HOSTS).toContain('aiplatform.googleapis.com');
expect(GOOGLE_API_HOSTS).toHaveLength(2);
});
});
});