import {Request} from '@loopback/rest'; import {LoggerConfig, SecurityValidationProvider} from '../providers/security-validator.provider'; type SecurityValidationOptions = { skip?: boolean; logger?: LoggerConfig; validateBody?: boolean; validateQuery?: boolean; validateParams?: boolean; } /** * LoopBack 4 Security validation decorator * Renamed from @validate to @securityValidation for better clarity * * For LoopBack 4, it's recommended to use interceptors instead * of this decorator for better integration with the framework. * * However, this decorator can still be used for method-level validation. * * Usage in LoopBack 4 controller: * ```typescript * export class UserController { * constructor( * @inject(RestBindings.Http.REQUEST) private request: Request, * ) {} * * @securityValidation() * @post('/users') * async createUser( * @requestBody() userData: CreateUserRequest, * ): Promise { * // Security validation happens automatically before this method executes * return this.userService.create(userData); * } * } * ``` * * @param options - Security validation options * @returns Method decorator function */ export function securityValidation(options: SecurityValidationOptions = {}) { return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { // Store the original method const originalMethod = descriptor.value; // Replace the method with security-enhanced version descriptor.value = async function (this: {request?: Request}, ...args: unknown[]) { // Skip validation if configured if (options.skip) { return originalMethod.apply(this, args); } try { // Get request object from context const request = this.request; if (!request) { // If no request object, proceed without validation (for non-HTTP methods) return originalMethod.apply(this, args); } // Create security validator instance const validator = new SecurityValidationProvider(); // Perform security validation based on options if (options.validateBody !== false) { await validator.validateRequestBody(request); } if (options.validateQuery !== false) { await validator.validateQueryParams(request); } if (options.validateParams !== false) { await validator.validateUrlParams(request); } // If validation passes, execute the original method return originalMethod.apply(this, args); } catch (error) { // Log the security violation console.error(`Security validation failed for ${propertyKey}:`, error); // Re-throw the error to be handled by the framework throw error; } }; return descriptor; }; } /** * Legacy decorator name for backward compatibility * @deprecated Use @securityValidation instead */ export function validate(options: SecurityValidationOptions = {}) { console.warn('@validate decorator is deprecated. Use @securityValidation instead.'); return securityValidation(options); } /** * Comprehensive security validation decorator with all security checks enabled */ export function comprehensiveSecurity(options: Omit = {}) { return securityValidation({ ...options, validateBody: true, validateQuery: true, validateParams: true }); } /** * Body-only security validation decorator */ export function bodySecurityValidation(options: Omit = {}) { return securityValidation({ ...options, validateQuery: false, validateParams: false }); } /** * Query-only security validation decorator */ export function querySecurityValidation(options: Omit = {}) { return securityValidation({ ...options, validateBody: false, validateParams: false }); } /** * Parameters-only security validation decorator */ export function paramsSecurityValidation(options: Omit = {}) { return securityValidation({ ...options, validateBody: false, validateQuery: false }); } /** * Security validation interceptor class for LoopBack 4 * This is the recommended approach for LoopBack 4 applications */ export class SecurityValidationInterceptor { private validator: SecurityValidationProvider; constructor() { this.validator = new SecurityValidationProvider(); } /** * Intercept and validate requests */ async intercept(request: Request): Promise { await this.validator.validateSecurityRequest(request); } /** * Get validator instance for direct use */ getValidator(): SecurityValidationProvider { return this.validator; } } // Export types for external use export type {LoggerConfig, SecurityValidationOptions};