fix(audio): add SSRF protection, MIME type fix, and tests for audio-transcribe tool
- Add URL validation blocking localhost, private IPs, and non-http protocols - Use response Content-Type header instead of hardcoded audio/wav for URL downloads - Add 25 tests covering validation, SSRF, config errors, transcription paths, and error handling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,31 @@ const PROVIDER_ENDPOINTS: Record<string, string> = {
|
||||
llamacpp: 'http://localhost:8080/v1/audio/transcriptions',
|
||||
};
|
||||
|
||||
function validateUrl(url: string): { valid: boolean; error?: string } {
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(url);
|
||||
} catch {
|
||||
return { valid: false, error: `Invalid URL: ${url}` };
|
||||
}
|
||||
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
||||
return { valid: false, error: `Only http/https URLs are allowed, got ${parsed.protocol}` };
|
||||
}
|
||||
|
||||
const hostname = parsed.hostname;
|
||||
if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '0.0.0.0') {
|
||||
return { valid: false, error: 'URLs pointing to localhost are not allowed' };
|
||||
}
|
||||
|
||||
// Block private/internal IP ranges
|
||||
if (/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|169\.254\.)/.test(hostname)) {
|
||||
return { valid: false, error: 'URLs pointing to private/internal networks are not allowed' };
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
function validateInput(args: AudioTranscribeArgs): { valid: boolean; error?: string } {
|
||||
const hasData = args.data !== undefined && args.data !== '';
|
||||
const hasUrl = args.url !== undefined && args.url !== '';
|
||||
@@ -45,6 +70,13 @@ function validateInput(args: AudioTranscribeArgs): { valid: boolean; error?: str
|
||||
return { valid: false, error: `Unsupported MIME type: ${args.mime_type}. Supported: ${Array.from(SUPPORTED_MIME_TYPES).join(', ')}` };
|
||||
}
|
||||
|
||||
if (hasUrl) {
|
||||
const urlValidation = validateUrl(args.url!);
|
||||
if (!urlValidation.valid) {
|
||||
return urlValidation;
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
@@ -136,7 +168,8 @@ export function createAudioTranscribeTool(audioConfig?: AudioTranscriptionConfig
|
||||
const urlExt = args.url.split('.').pop()?.split('?')[0] || 'bin';
|
||||
filename = `audio.${urlExt}`;
|
||||
|
||||
audioBlob = new Blob([arrayBuffer], { type: 'audio/wav' });
|
||||
const contentType = response.headers.get('content-type') ?? 'audio/wav';
|
||||
audioBlob = new Blob([arrayBuffer], { type: contentType });
|
||||
}
|
||||
|
||||
const endpoint = audioConfig.endpoint;
|
||||
|
||||
Reference in New Issue
Block a user