mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-10 11:34:23 +01:00
Merge commit from fork
- Implemented validation for OpenAPI specifications to ensure the server URL matches the client-provided domain, preventing SSRF attacks. - Added domain extraction and validation functions to improve security checks. - Updated relevant services and routes to utilize the new validation logic, ensuring robust handling of client-provided domains against the OpenAPI spec. - Introduced comprehensive tests to validate the new security features and ensure correct behavior across various scenarios.
This commit is contained in:
parent
4e4c8d0c0e
commit
b6ba2711f9
4 changed files with 497 additions and 2 deletions
|
|
@ -563,8 +563,83 @@ export type ValidationResult = {
|
|||
status: boolean;
|
||||
message: string;
|
||||
spec?: OpenAPIV3.Document;
|
||||
serverUrl?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the domain from a URL string.
|
||||
* @param {string} url - The URL to extract the domain from.
|
||||
* @returns {string} The extracted domain (hostname with protocol).
|
||||
*/
|
||||
export function extractDomainFromUrl(url: string): string {
|
||||
try {
|
||||
const parsedUrl = new URL(url);
|
||||
// Return protocol + hostname (e.g., "https://example.com")
|
||||
// This preserves the protocol which is important for SSRF prevention
|
||||
return `${parsedUrl.protocol}//${parsedUrl.hostname}`;
|
||||
} catch {
|
||||
throw new Error(`Invalid URL format: ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
export type DomainValidationResult = {
|
||||
isValid: boolean;
|
||||
message?: string;
|
||||
normalizedSpecDomain?: string;
|
||||
normalizedClientDomain?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates that a client-provided domain matches the domain from an OpenAPI spec server URL.
|
||||
* This is critical for preventing SSRF attacks where an attacker provides a whitelisted domain
|
||||
* but uses a different (potentially internal) URL in the raw OpenAPI spec.
|
||||
*
|
||||
* @param {string} clientProvidedDomain - The domain provided by the client (may or may not include protocol)
|
||||
* @param {string} specServerUrl - The server URL from the OpenAPI spec
|
||||
* @returns {DomainValidationResult} Validation result with normalized domains
|
||||
*/
|
||||
export function validateActionDomain(
|
||||
clientProvidedDomain: string,
|
||||
specServerUrl: string,
|
||||
): DomainValidationResult {
|
||||
try {
|
||||
// Extract domain from the spec's server URL
|
||||
const specDomain = extractDomainFromUrl(specServerUrl);
|
||||
const normalizedSpecDomain = extractDomainFromUrl(specDomain);
|
||||
|
||||
// Normalize client-provided domain (add https:// if no protocol)
|
||||
const normalizedClientDomain = clientProvidedDomain.startsWith('http')
|
||||
? clientProvidedDomain
|
||||
: `https://${clientProvidedDomain}`;
|
||||
|
||||
// Compare normalized domains
|
||||
// We check both the normalized client domain and the raw client domain
|
||||
// to handle cases where the client might provide "example.com" vs "https://example.com"
|
||||
if (
|
||||
normalizedSpecDomain !== normalizedClientDomain &&
|
||||
normalizedSpecDomain !== clientProvidedDomain
|
||||
) {
|
||||
return {
|
||||
isValid: false,
|
||||
message: `Domain mismatch: Client provided '${clientProvidedDomain}', but spec uses '${normalizedSpecDomain}'`,
|
||||
normalizedSpecDomain,
|
||||
normalizedClientDomain,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
normalizedSpecDomain,
|
||||
normalizedClientDomain,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
isValid: false,
|
||||
message: `Failed to validate domain: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and parses an OpenAPI spec.
|
||||
*/
|
||||
|
|
@ -626,6 +701,7 @@ export function validateAndParseOpenAPISpec(specString: string): ValidationResul
|
|||
status: true,
|
||||
message: messages.join('\n') || 'OpenAPI spec is valid.',
|
||||
spec: parsedSpec,
|
||||
serverUrl: parsedSpec.servers[0].url,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue