UNPKG

defect-inspection-tools-mcp-server

Version:

Defect inspection tools server handling defect detection, analysis, and reporting with AI/ML capabilities for quality control

745 lines 29.5 kB
import { logger } from '../utils/logger.js'; export class ValidationMiddleware { static validateInput(toolName, args) { const result = { isValid: true, errors: [], sanitizedArgs: { ...args } }; try { // Common validations for all tools this.validateCommonFields(result); // Tool-specific validations switch (toolName) { case 'universal_defect_search': this.validateUniversalDefectSearch(result); break; case 'list_defects_by_stage': this.validateDefectsByStage(result); break; case 'list_defects_by_inspection_type': this.validateDefectsByInspectionType(result); break; case 'list_defects_by_report_type': this.validateDefectsByReportType(result); break; case 'list_defects_by_status': this.validateDefectsByStatus(result); break; case 'get_all_vessel_defects_records': this.validateVesselDefectsRecords(result); break; case 'get_vessel_details': this.validateVesselDetails(result); break; case 'get_defect_table_schema': this.validateDefectTableSchema(result); break; case 'get_tmsa_summary': this.validateTmsaSummary(result); break; case 'get_sire_reports_from_ocimf': this.validateSireReports(result); break; case 'fetch_fleet_historical_sire_observations': this.validateFleetHistoricalSire(result); break; case 'get_cdi_reports_from_ocimf': this.validateCdiReports(result); break; case 'get_vir_status_overview': this.validateVirStatus(result); break; case 'get_internal_audit_summary': this.validateInternalAudit(result); break; case 'get_psc_inspection_defects': this.validatePscInspection(result); break; case 'get_summary_of_defects': this.validateSummaryOfDefects(result); break; case 'google_search': this.validateGoogleSearch(result); break; case 'parse_document_link': this.validateDocumentLink(result); break; case 'write_casefile_data': this.validateWriteCasefileData(result); break; case 'retrieve_casefile_data': this.validateRetrieveCasefileData(result); break; default: result.errors.push({ field: 'tool', message: `Unknown tool: ${toolName}` }); break; } result.isValid = result.errors.length === 0; if (!result.isValid) { logger.warn(`Validation failed for tool ${toolName}`, { errors: result.errors, args: args }); } return result; } catch (error) { logger.error('Validation middleware error:', error); result.isValid = false; result.errors.push({ field: 'validation', message: 'Internal validation error' }); return result; } } static validateCommonFields(result) { // Validate session_id if present if (result.sanitizedArgs.session_id !== undefined) { const sessionIdValidation = this.validateString(result.sanitizedArgs.session_id, 'session_id', { maxLength: 100 }); if (!sessionIdValidation.isValid) { result.errors.push(...sessionIdValidation.errors); } else { result.sanitizedArgs.session_id = sessionIdValidation.value; } } // Validate limit if present if (result.sanitizedArgs.limit !== undefined) { const limitValidation = this.validateNumber(result.sanitizedArgs.limit, 'limit', { min: 1, max: 1000, integer: true }); if (!limitValidation.isValid) { result.errors.push(...limitValidation.errors); } else { result.sanitizedArgs.limit = limitValidation.value; } } // Validate offset if present if (result.sanitizedArgs.offset !== undefined) { const offsetValidation = this.validateNumber(result.sanitizedArgs.offset, 'offset', { min: 0, integer: true }); if (!offsetValidation.isValid) { result.errors.push(...offsetValidation.errors); } else { result.sanitizedArgs.offset = offsetValidation.value; } } } // IMO number validation - critical for maritime applications static validateIMO(value, fieldName, required = false) { const errors = []; if (value === null || value === undefined) { if (required) { errors.push({ field: fieldName, message: 'IMO number is required', value }); } return { isValid: !required, errors }; } // Check if value is a string or number that contains only digits const valueStr = String(value).trim(); // Empty string should be invalid if (valueStr === '') { errors.push({ field: fieldName, message: 'IMO number cannot be empty', value }); return { isValid: false, errors }; } const digitOnlyRegex = /^\d+$/; if (!digitOnlyRegex.test(valueStr)) { errors.push({ field: fieldName, message: 'IMO number must contain only digits', value }); return { isValid: false, errors }; } const imoNumber = parseInt(value, 10); if (isNaN(imoNumber) || imoNumber <= 0) { errors.push({ field: fieldName, message: 'IMO number must be a positive integer', value }); return { isValid: false, errors }; } return { isValid: true, value: imoNumber, errors: [] }; } // Date range validation with proper chronological order static validateDateRange(startDate, endDate, startFieldName = 'start_date', endFieldName = 'end_date') { const errors = []; let parsedStartDate; let parsedEndDate; if (startDate) { parsedStartDate = new Date(startDate); if (isNaN(parsedStartDate.getTime())) { errors.push({ field: startFieldName, message: 'Invalid start date format', value: startDate }); } } if (endDate) { parsedEndDate = new Date(endDate); if (isNaN(parsedEndDate.getTime())) { errors.push({ field: endFieldName, message: 'Invalid end date format', value: endDate }); } } if (parsedStartDate && parsedEndDate && parsedStartDate > parsedEndDate) { errors.push({ field: endFieldName, message: 'End date must be after start date', value: endDate }); } return { isValid: errors.length === 0, startDate: parsedStartDate, endDate: parsedEndDate, errors }; } // String validation with length limits and sanitization static validateString(value, fieldName, options = {}) { const errors = []; if (value === null || value === undefined || value === '') { if (options.required) { errors.push({ field: fieldName, message: `${fieldName} is required`, value }); } return { isValid: !options.required, errors }; } if (typeof value !== 'string') { value = String(value); } // Sanitize the string const sanitizedValue = value.trim(); if (options.minLength && sanitizedValue.length < options.minLength) { errors.push({ field: fieldName, message: `${fieldName} must be at least ${options.minLength} characters`, value }); } if (options.maxLength && sanitizedValue.length > options.maxLength) { errors.push({ field: fieldName, message: `${fieldName} cannot exceed ${options.maxLength} characters`, value }); } if (options.pattern && !options.pattern.test(sanitizedValue)) { errors.push({ field: fieldName, message: `${fieldName} format is invalid`, value }); } return { isValid: errors.length === 0, value: sanitizedValue, errors }; } // Number validation with bounds checking static validateNumber(value, fieldName, options = {}) { const errors = []; if (value === null || value === undefined) { if (options.required) { errors.push({ field: fieldName, message: `${fieldName} is required`, value }); } return { isValid: !options.required, errors }; } const numValue = Number(value); if (isNaN(numValue)) { errors.push({ field: fieldName, message: `${fieldName} must be a valid number`, value }); return { isValid: false, errors }; } if (options.integer && !Number.isInteger(numValue)) { errors.push({ field: fieldName, message: `${fieldName} must be an integer`, value }); } if (options.min !== undefined && numValue < options.min) { errors.push({ field: fieldName, message: `${fieldName} must be at least ${options.min}`, value }); } if (options.max !== undefined && numValue > options.max) { errors.push({ field: fieldName, message: `${fieldName} cannot exceed ${options.max}`, value }); } return { isValid: errors.length === 0, value: numValue, errors }; } // Array validation with item limits static validateArray(value, fieldName, options = {}) { const errors = []; if (value === null || value === undefined) { if (options.required) { errors.push({ field: fieldName, message: `${fieldName} is required`, value }); } return { isValid: !options.required, errors }; } if (!Array.isArray(value)) { errors.push({ field: fieldName, message: `${fieldName} must be an array`, value }); return { isValid: false, errors }; } if (options.minItems !== undefined && value.length < options.minItems) { errors.push({ field: fieldName, message: `${fieldName} must have at least ${options.minItems} items`, value }); } if (options.maxItems !== undefined && value.length > options.maxItems) { errors.push({ field: fieldName, message: `${fieldName} cannot have more than ${options.maxItems} items`, value }); } return { isValid: errors.length === 0, value: value, errors }; } // Tool-specific validation methods static validateUniversalDefectSearch(result) { const args = result.sanitizedArgs; if (args.search_type) { const searchTypeValidation = this.validateString(args.search_type, 'search_type', { maxLength: 50 }); if (!searchTypeValidation.isValid) { result.errors.push(...searchTypeValidation.errors); } else { result.sanitizedArgs.search_type = searchTypeValidation.value; } } if (args.query) { const queryValidation = this.validateString(args.query, 'query', { maxLength: 1000 }); if (!queryValidation.isValid) { result.errors.push(...queryValidation.errors); } else { result.sanitizedArgs.query = queryValidation.value; } } if (args.sort_by) { const validSortOptions = ['relevance', 'date', 'severity', 'status']; const sortByValidation = this.validateString(args.sort_by, 'sort_by', { maxLength: 20 }); if (!sortByValidation.isValid) { result.errors.push(...sortByValidation.errors); } else if (!validSortOptions.includes(sortByValidation.value)) { result.errors.push({ field: 'sort_by', message: `sort_by must be one of: ${validSortOptions.join(', ')}`, value: args.sort_by }); } else { result.sanitizedArgs.sort_by = sortByValidation.value; } } if (args.sort_order) { const validSortOrders = ['asc', 'desc']; const sortOrderValidation = this.validateString(args.sort_order, 'sort_order', { maxLength: 10 }); if (!sortOrderValidation.isValid) { result.errors.push(...sortOrderValidation.errors); } else if (!validSortOrders.includes(sortOrderValidation.value)) { result.errors.push({ field: 'sort_order', message: `sort_order must be one of: ${validSortOrders.join(', ')}`, value: args.sort_order }); } else { result.sanitizedArgs.sort_order = sortOrderValidation.value; } } } static validateVesselDetails(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateDefectsByStage(result) { const args = result.sanitizedArgs; if (args.stage) { const stageValidation = this.validateString(args.stage, 'stage', { maxLength: 50 }); if (!stageValidation.isValid) { result.errors.push(...stageValidation.errors); } else { result.sanitizedArgs.stage = stageValidation.value; } } if (args.start_date && args.end_date) { const dateValidation = this.validateDateRange(args.start_date, args.end_date); if (!dateValidation.isValid) { result.errors.push(...dateValidation.errors); } else { result.sanitizedArgs.start_date = dateValidation.startDate?.toISOString(); result.sanitizedArgs.end_date = dateValidation.endDate?.toISOString(); } } } static validateDefectsByInspectionType(result) { const args = result.sanitizedArgs; if (args.inspection_type) { const inspectionTypeValidation = this.validateString(args.inspection_type, 'inspection_type', { maxLength: 50 }); if (!inspectionTypeValidation.isValid) { result.errors.push(...inspectionTypeValidation.errors); } else { result.sanitizedArgs.inspection_type = inspectionTypeValidation.value; } } } static validateDefectsByReportType(result) { const args = result.sanitizedArgs; if (args.report_type) { const reportTypeValidation = this.validateString(args.report_type, 'report_type', { maxLength: 50 }); if (!reportTypeValidation.isValid) { result.errors.push(...reportTypeValidation.errors); } else { result.sanitizedArgs.report_type = reportTypeValidation.value; } } } static validateDefectsByStatus(result) { const args = result.sanitizedArgs; if (args.status) { const statusValidation = this.validateString(args.status, 'status', { maxLength: 50 }); if (!statusValidation.isValid) { result.errors.push(...statusValidation.errors); } else { result.sanitizedArgs.status = statusValidation.value; } } } static validateVesselDefectsRecords(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateDefectTableSchema(result) { const args = result.sanitizedArgs; if (args.table_name) { const tableNameValidation = this.validateString(args.table_name, 'table_name', { maxLength: 100 }); if (!tableNameValidation.isValid) { result.errors.push(...tableNameValidation.errors); } else { result.sanitizedArgs.table_name = tableNameValidation.value; } } } static validateTmsaSummary(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateSireReports(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateFleetHistoricalSire(result) { const args = result.sanitizedArgs; if (args.fleet_name) { const fleetNameValidation = this.validateString(args.fleet_name, 'fleet_name', { maxLength: 100 }); if (!fleetNameValidation.isValid) { result.errors.push(...fleetNameValidation.errors); } else { result.sanitizedArgs.fleet_name = fleetNameValidation.value; } } if (args.start_date && args.end_date) { const dateValidation = this.validateDateRange(args.start_date, args.end_date); if (!dateValidation.isValid) { result.errors.push(...dateValidation.errors); } else { result.sanitizedArgs.start_date = dateValidation.startDate?.toISOString(); result.sanitizedArgs.end_date = dateValidation.endDate?.toISOString(); } } } static validateCdiReports(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateVirStatus(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateInternalAudit(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validatePscInspection(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateSummaryOfDefects(result) { const args = result.sanitizedArgs; if (args.imo_number !== null && args.imo_number !== undefined) { const imoValidation = this.validateIMO(args.imo_number, 'imo_number'); if (!imoValidation.isValid) { result.errors.push(...imoValidation.errors); } else { result.sanitizedArgs.imo_number = imoValidation.value; } } if (args.vessel_name) { const vesselNameValidation = this.validateString(args.vessel_name, 'vessel_name', { maxLength: 100 }); if (!vesselNameValidation.isValid) { result.errors.push(...vesselNameValidation.errors); } else { result.sanitizedArgs.vessel_name = vesselNameValidation.value; } } } static validateGoogleSearch(result) { const args = result.sanitizedArgs; if (args.query) { const queryValidation = this.validateString(args.query, 'query', { maxLength: 500, required: true }); if (!queryValidation.isValid) { result.errors.push(...queryValidation.errors); } else { result.sanitizedArgs.query = queryValidation.value; } } } static validateDocumentLink(result) { const args = result.sanitizedArgs; if (args.document_url) { const urlValidation = this.validateString(args.document_url, 'document_url', { maxLength: 2000, required: true }); if (!urlValidation.isValid) { result.errors.push(...urlValidation.errors); } else { result.sanitizedArgs.document_url = urlValidation.value; } } } static validateWriteCasefileData(result) { const args = result.sanitizedArgs; if (args.case_id) { const caseIdValidation = this.validateString(args.case_id, 'case_id', { maxLength: 100, required: true }); if (!caseIdValidation.isValid) { result.errors.push(...caseIdValidation.errors); } else { result.sanitizedArgs.case_id = caseIdValidation.value; } } if (args.data) { const dataValidation = this.validateString(args.data, 'data', { maxLength: 10000, required: true }); if (!dataValidation.isValid) { result.errors.push(...dataValidation.errors); } else { result.sanitizedArgs.data = dataValidation.value; } } } static validateRetrieveCasefileData(result) { const args = result.sanitizedArgs; if (args.case_id) { const caseIdValidation = this.validateString(args.case_id, 'case_id', { maxLength: 100, required: true }); if (!caseIdValidation.isValid) { result.errors.push(...caseIdValidation.errors); } else { result.sanitizedArgs.case_id = caseIdValidation.value; } } } } //# sourceMappingURL=validation-middleware.js.map