Skip to main content
Glama
batch-validation.tsβ€’7.89 kB
/** * Batch operation validation utilities for DoS protection * * Provides comprehensive validation for batch operations including * size limits, payload validation, and rate limiting checks. */ import { BATCH_SIZE_LIMITS, PAYLOAD_SIZE_LIMITS, LIMIT_ERROR_MESSAGES, getBatchSizeLimit, } from '../config/security-limits.js'; import { ErrorType } from './error-handler.js'; import { UnknownObject } from './types/common.js'; /** * Validation result for batch operations */ export interface BatchValidationResult { isValid: boolean; error?: string; errorType?: ErrorType; details?: { actualSize?: number; maxSize?: number; payloadSize?: number; maxPayloadSize?: number; }; } /** * Calculate the approximate size of a JavaScript object in bytes * This is used to estimate payload sizes for validation */ function getObjectSize(obj: unknown): number { let size = 0; if (obj === null || obj === undefined) { return 0; } if (typeof obj === 'string') { return obj.length * 2; // Unicode characters can be up to 2 bytes } if (typeof obj === 'number') { return 8; // Numbers are typically 8 bytes } if (typeof obj === 'boolean') { return 4; // Booleans are typically 4 bytes } if (obj instanceof Date) { return 8; // Dates are stored as numbers } if (Array.isArray(obj)) { for (const item of obj) { size += getObjectSize(item); } return size; } if (typeof obj === 'object') { for (const [key, value] of Object.entries(obj)) { size += key.length * 2; // Key size size += getObjectSize(value); // Value size } return size; } return 0; } /** * Validates the size of a batch operation * * @param items - Array of items in the batch * @param operationType - Type of operation (create, update, delete, etc.) * @param resourceType - Type of resource (companies, people, etc.) * @returns Validation result */ export function validateBatchSize( items: unknown[] | undefined | null, operationType: string, resourceType?: string ): BatchValidationResult { // Check if items is a valid array if (!items || !Array.isArray(items)) { return { isValid: false, error: 'Batch items must be a non-empty array', errorType: ErrorType.VALIDATION_ERROR, }; } // Check for empty array if (items.length === 0) { return { isValid: false, error: 'Batch operation requires at least one item', errorType: ErrorType.VALIDATION_ERROR, }; } // Get the appropriate size limit let maxSize = getBatchSizeLimit(resourceType); // Apply more restrictive limits for certain operations if (operationType.toLowerCase() === 'delete') { maxSize = Math.min(maxSize, BATCH_SIZE_LIMITS.DELETE); } else if (operationType.toLowerCase() === 'search') { maxSize = Math.min(maxSize, BATCH_SIZE_LIMITS.SEARCH); } // Check if batch size exceeds limit if (items.length > maxSize) { return { isValid: false, error: LIMIT_ERROR_MESSAGES.BATCH_SIZE_EXCEEDED( items.length, maxSize, `${operationType} ${resourceType || ''}`.trim() ), errorType: ErrorType.VALIDATION_ERROR, details: { actualSize: items.length, maxSize, }, }; } return { isValid: true }; } /** * Validates the payload size of a batch operation * * @param payload - The payload to validate * @param checkSingleRecords - Whether to check individual record sizes * @returns Validation result */ export function validatePayloadSize( payload: unknown, checkSingleRecords: boolean = true ): BatchValidationResult { // Calculate total payload size const totalSize = getObjectSize(payload); // Check total payload size if (totalSize > PAYLOAD_SIZE_LIMITS.BATCH_TOTAL) { return { isValid: false, error: LIMIT_ERROR_MESSAGES.PAYLOAD_SIZE_EXCEEDED( totalSize, PAYLOAD_SIZE_LIMITS.BATCH_TOTAL ), errorType: ErrorType.VALIDATION_ERROR, details: { payloadSize: totalSize, maxPayloadSize: PAYLOAD_SIZE_LIMITS.BATCH_TOTAL, }, }; } // Check individual record sizes if requested if (checkSingleRecords && Array.isArray(payload)) { for (let i = 0; i < payload.length; i++) { const recordSize = getObjectSize(payload[i]); if (recordSize > PAYLOAD_SIZE_LIMITS.SINGLE_RECORD) { return { isValid: false, error: `Record at index ${i}: ` + LIMIT_ERROR_MESSAGES.SINGLE_RECORD_SIZE_EXCEEDED( recordSize, PAYLOAD_SIZE_LIMITS.SINGLE_RECORD ), errorType: ErrorType.VALIDATION_ERROR, details: { payloadSize: recordSize, maxPayloadSize: PAYLOAD_SIZE_LIMITS.SINGLE_RECORD, }, }; } } } return { isValid: true }; } /** * Validates search query parameters * * @param query - Search query string * @param filters - Optional filter object * @returns Validation result */ export function validateSearchQuery( query?: string, filters?: UnknownObject ): BatchValidationResult { // Validate query string length if (query && query.length > PAYLOAD_SIZE_LIMITS.SEARCH_QUERY) { return { isValid: false, error: LIMIT_ERROR_MESSAGES.SEARCH_QUERY_TOO_LONG( query.length, PAYLOAD_SIZE_LIMITS.SEARCH_QUERY ), errorType: ErrorType.VALIDATION_ERROR, }; } // Validate filter object size if (filters) { const filterSize = getObjectSize(filters); if (filterSize > PAYLOAD_SIZE_LIMITS.FILTER_OBJECT) { return { isValid: false, error: LIMIT_ERROR_MESSAGES.FILTER_TOO_COMPLEX( filterSize, PAYLOAD_SIZE_LIMITS.FILTER_OBJECT ), errorType: ErrorType.VALIDATION_ERROR, }; } } return { isValid: true }; } /** * Comprehensive validation for batch operations * Combines size, payload, and other validations * * @param params - Parameters object containing batch operation details * @returns Validation result */ export function validateBatchOperation(params: { items?: unknown[]; operationType: string; resourceType?: string; checkPayload?: boolean; }): BatchValidationResult { const { items, operationType, resourceType, checkPayload = true } = params; // Validate batch size const sizeValidation = validateBatchSize(items, operationType, resourceType); if (!sizeValidation.isValid) { return sizeValidation; } // Validate payload size if requested if (checkPayload && items) { const payloadValidation = validatePayloadSize(items); if (!payloadValidation.isValid) { return payloadValidation; } } return { isValid: true }; } /** * Splits a large batch into smaller chunks that respect size limits * * @param items - Array of items to split * @param resourceType - Type of resource for determining limits * @returns Array of batches */ export function splitBatchIntoChunks<T>( items: T[], resourceType?: string ): T[][] { if (!items || items.length === 0) { return []; } const maxSize = getBatchSizeLimit(resourceType); const chunks: T[][] = []; for (let i = 0; i < items.length; i += maxSize) { chunks.push(items.slice(i, i + maxSize)); } return chunks; } /** * Creates a safe error message for batch validation failures * This ensures no sensitive information is exposed in error messages * * @param validation - Validation result * @returns Safe error message */ export function createSafeBatchError( validation: BatchValidationResult ): string { if (validation.isValid) { return ''; } // Return the error message without exposing internal limits // The error messages already handle this safely return validation.error || 'Batch validation failed'; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server