Comprehensive security middleware with layered protection architecture

Production-ready security middleware implementing defense-in-depth strategy. Two-phase processing optimizes performance while providing comprehensive protection. Zero-dependency implementation ensures supply chain security.

Architecture: Pre-processing (blocking) → Route handling → Post-processing (headers) Performance: Early exit on security violations, minimal overhead for clean requests Memory: Rate limiting with automatic cleanup, no memory leaks in long-running apps Integration: Drop-in middleware compatible with Wings router and standard HTTP

1. IP Access Control: Whitelist/blacklist with CIDR support and proxy-aware IP extraction 2. Rate Limiting: Sliding window algorithm with per-route customization and memory management 3. Request Validation: Size and structure limits preventing DoS via resource exhaustion 4. Attack Detection: Pattern matching for SQL injection, XSS, path traversal, command injection 5. Security Headers: CSP, HSTS, COEP/COOP and modern browser protections

import { Armor } from '@raven-js/wings/server/middlewares/armor';

const armor = new Armor();
router.use(armor);
// Enables: security headers, request validation, attack detection
// Disabled: rate limiting, IP filtering (configure as needed)
const armor = new Armor({
rateLimiting: {
enabled: true,
global: { max: 1000, windowMs: 60 * 60 * 1000 }, // 1000/hour
routes: {
'/api/auth/login': { max: 5, windowMs: 15 * 60 * 1000 }, // 5/15min
'/api/': { max: 100, windowMs: 60 * 1000 } // 100/minute
}
},
ipAccess: {
mode: 'blacklist',
blacklist: ['192.168.1.100', '10.0.0.0/8'],
trustProxy: true // Behind load balancer
},
securityHeaders: {
contentSecurityPolicy: {
'default-src': ["'self'"],
'script-src': ["'self'", 'https://cdn.example.com'],
'style-src': ["'self'", "'unsafe-inline'"]
},
httpStrictTransportSecurity: {
maxAge: 63072000, // 2 years
includeSubDomains: true,
preload: true
}
}
});
const armor = new Armor({
securityHeaders: {
contentSecurityPolicy: false, // Disable for dev tools
httpStrictTransportSecurity: false // No HTTPS in dev
},
rateLimiting: { enabled: false }, // No rate limits in dev
attackDetection: {
sqlInjection: false, // Allow test payloads
suspiciousPatterns: false
}
});
const armor = new Armor({
rateLimiting: {
enabled: true,
keyGenerator: (ctx) => {
// Custom rate limiting by API key + IP
const apiKey = ctx.requestHeaders.get('x-api-key') || 'anonymous';
const ip = getClientIP(ctx, true);
return `${apiKey}:${ip}`;
},
global: { max: 10000, windowMs: 60 * 60 * 1000 }
},
requestValidation: {
maxBodySize: 10 * 1024 * 1024, // 10MB for file uploads
maxHeaders: 50 // Fewer headers for API
}
});

Request Processing: O(1) for most operations, O(n) for rate limit cleanup Memory Usage: O(k×r) where k = unique rate limit keys, r = requests per window Cleanup Overhead: Periodic O(k) cleanup prevents memory accumulation Security Trade-offs: Pattern matching CPU cost vs attack detection coverage

Security events logged to ctx.errors array for centralized monitoring:

  • IPBlocked: IP address denied by access control
  • RateLimitExceeded: Request rate limit violation
  • RequestValidationError: Oversized or malformed request
  • AttackPatternDetected: Suspicious pattern in request data
  • SecurityHeaderError: Header setting failure (non-blocking)
  • ArmorError: General security processing failure

Hierarchy

  • Middleware
    • Armor

Constructors

  • Create a new Armor middleware instance with validated configuration. Merges user configuration with secure defaults and validates all settings.

    Configuration Validation: Comprehensive validation prevents runtime security failures Default Security: Conservative defaults provide immediate protection Memory Initialization: Rate limiting store created only when enabled Error Prevention: Invalid configurations rejected at construction time

    Parameters

    • OptionaluserConfig: Partial<ArmorConfig> = {}

      Security configuration overrides

    • Optionalidentifier: string = "@raven-js/wings/armor"

      Middleware identifier for debugging

    Returns Armor

    When configuration validation fails

Properties

config: any
rateLimitStore: RateLimitStore

Accessors

  • get handler(): Handler
  • Gets the middleware handler function.

    Returns the function that will be executed when the middleware runs. This getter provides read-only access to the handler function.

    Returns Handler

    The handler function

    const middleware = new Middleware((ctx) => {
    console.log('Processing request');
    });

    const handler = middleware.handler;
    console.log(typeof handler); // 'function'

    // Can be used for testing or introspection
    if (handler.toString().includes('console.log')) {
    console.log('This middleware includes logging');
    }
  • get identifier(): string
  • Gets the middleware identifier.

    Returns the identifier string or null if no identifier was set. This getter provides read-only access to the identifier.

    Returns string

    The identifier or null if not set

    // Middleware with identifier
    const authMiddleware = new Middleware((ctx) => {}, 'auth');
    console.log(authMiddleware.identifier); // 'auth'

    // Middleware without identifier
    const loggingMiddleware = new Middleware((ctx) => {});
    console.log(loggingMiddleware.identifier); // null

    // Use identifier for conditional logic
    if (middleware.identifier === 'authentication') {
    console.log('This is an authentication middleware');
    }

Methods

  • Clear all rate limiting data immediately (primarily for testing). Removes all stored request history, effectively resetting all rate limits.

    Use Cases: Testing, emergency reset, configuration changes requiring clean slate Production Warning: All clients get fresh rate limit allowances immediately Performance: O(1) operation, immediate memory reclamation Thread Safety: Safe to call during request processing

    Returns void

  • Executes the middleware handler with the given context.

    This method invokes the middleware handler function, passing the context object as the first argument. The method supports both synchronous and asynchronous handlers, always returning a Promise.

    Execution Flow:

    1. Validates that ctx is a Context instance
    2. Calls the handler function with the context
    3. Returns a Promise that resolves when the handler completes
    4. Propagates any errors thrown by the handler

    Error Handling: Any errors thrown by the handler (synchronous or asynchronous) are propagated to the caller. This allows middleware errors to be handled by the calling code.

    Parameters

    • ctx: Context

      The request/response context

    Returns Promise<void>

    Promise that resolves when the handler completes

    When ctx is not a Context instance

    Any error thrown by the middleware handler

    const middleware = new Middleware((ctx) => {
    console.log(`Processing ${ctx.method} request to ${ctx.path}`);
    ctx.data.processedAt = new Date().toISOString();
    });

    const url = new URL('http://localhost/api/users');
    const ctx = new Context('GET', url, new Headers());

    // Execute middleware
    await middleware.execute(ctx);
    console.log(ctx.data.processedAt); // ISO timestamp

    // Async middleware
    const asyncMiddleware = new Middleware(async (ctx) => {
    await new Promise(resolve => setTimeout(resolve, 100));
    ctx.data.asyncProcessed = true;
    });

    await asyncMiddleware.execute(ctx);
    console.log(ctx.data.asyncProcessed); // true

    // Error handling
    const errorMiddleware = new Middleware((ctx) => {
    throw new Error('Middleware error');
    });

    try {
    await errorMiddleware.execute(ctx);
    } catch (error) {
    console.error('Middleware failed:', error.message);
    }

    // Invalid context
    try {
    await middleware.execute(null);
    } catch (error) {
    console.error(error.message); // "Context must be a Context instance"
    }
  • Get current middleware statistics for monitoring and capacity planning. Provides insights into security state and memory usage.

    Monitoring Use: Track security events, memory usage, configuration state Performance Warning: Rate limit stats require O(k×n) computation Production Use: Call sparingly or cache results for high-traffic applications Capacity Planning: Use for rate limit memory estimation and tuning

    Returns {
        config: object;
        rateLimit: {
            totalKeys: number;
            totalRequests: number;
        };
    }

    Current statistics

    • config: object
    • rateLimit: {
          totalKeys: number;
          totalRequests: number;
      }
      • totalKeys: number
      • totalRequests: number
  • Checks if this middleware has the same identifier as another middleware.

    This method is used for duplicate detection and middleware comparison. Two middleware are considered to have the same identifier if:

    • Both have non-null identifiers
    • Both identifiers are exactly equal (string comparison)

    Note: Middleware with null identifiers are never considered equal, even if both have null identifiers.

    Parameters

    • other: Middleware

      The other middleware to compare with

    Returns boolean

    True if both middlewares have the same non-null identifier

    const auth1 = new Middleware((ctx) => {}, 'authentication');
    const auth2 = new Middleware((ctx) => {}, 'authentication');
    const logging = new Middleware((ctx) => {}, 'logging');
    const generic = new Middleware((ctx) => {});

    // Same identifier
    auth1.hasSameIdentifier(auth2); // true
    auth2.hasSameIdentifier(auth1); // true

    // Different identifiers
    auth1.hasSameIdentifier(logging); // false
    logging.hasSameIdentifier(auth1); // false

    // Null identifiers (never equal)
    auth1.hasSameIdentifier(generic); // false
    generic.hasSameIdentifier(auth1); // false
    generic.hasSameIdentifier(new Middleware((ctx) => {})); // false

    // Invalid parameters
    auth1.hasSameIdentifier(null); // false
    auth1.hasSameIdentifier({}); // false
    auth1.hasSameIdentifier('string'); // false

    // Use for duplicate prevention
    function addMiddlewareIfNotExists(middlewareList, newMiddleware) {
    const hasDuplicate = middlewareList.some(existing =>
    existing.hasSameIdentifier(newMiddleware)
    );

    if (!hasDuplicate) {
    middlewareList.push(newMiddleware);
    }
    }