Skip to main content
Glama
bradcstevens

Copilot Studio Agent Direct Line MCP Server

by bradcstevens
security-config.ts8.64 kB
/** * Production security configuration */ import { randomBytes } from 'crypto'; import type { Request, Response, NextFunction } from 'express'; /** * HTTPS enforcement middleware * Redirects HTTP requests to HTTPS in production */ export function enforceHTTPS(req: Request, res: Response, next: NextFunction): void { // Skip if already HTTPS if (req.secure || req.headers['x-forwarded-proto'] === 'https') { return next(); } // Skip in development if (process.env.NODE_ENV !== 'production') { return next(); } // Redirect to HTTPS const httpsUrl = `https://${req.headers.host}${req.url}`; console.warn(`[Security] Redirecting HTTP request to HTTPS: ${req.url}`); res.redirect(301, httpsUrl); } /** * Enhanced Helmet security headers configuration */ export const helmetConfig = { // Content Security Policy contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'"], // Allow inline scripts for SSE client styleSrc: ["'self'", "'unsafe-inline'"], // Allow inline styles imgSrc: ["'self'", 'data:', 'https:'], connectSrc: ["'self'"], // Restrict AJAX/SSE connections fontSrc: ["'self'"], objectSrc: ["'none'"], // Disable plugins mediaSrc: ["'self'"], frameSrc: ["'none'"], // Disable iframes frameAncestors: ["'none'"], // Prevent clickjacking baseUri: ["'self'"], formAction: ["'self'"], upgradeInsecureRequests: process.env.NODE_ENV === 'production' ? [] : null, }, }, // HTTP Strict Transport Security (HSTS) hsts: { maxAge: 31536000, // 1 year includeSubDomains: true, preload: true, }, // X-Frame-Options frameguard: { action: 'deny', }, // X-Content-Type-Options noSniff: true, // X-Download-Options ieNoOpen: true, // X-Permitted-Cross-Domain-Policies permittedCrossDomainPolicies: { permittedPolicies: 'none', }, // Referrer-Policy referrerPolicy: { policy: 'strict-origin-when-cross-origin', }, // X-DNS-Prefetch-Control dnsPrefetchControl: { allow: false, }, // X-XSS-Protection (legacy, but still useful) xssFilter: true, }; /** * Secure cookie configuration */ export const secureCookieConfig = { httpOnly: true, // Prevent JavaScript access secure: process.env.NODE_ENV === 'production', // HTTPS only in production sameSite: 'strict' as const, // CSRF protection maxAge: 24 * 60 * 60 * 1000, // 24 hours signed: true, // Sign cookies domain: process.env.COOKIE_DOMAIN, // Set domain if needed path: '/', }; /** * Session configuration with enhanced security * Automatically generates a secure random session secret if not provided */ export const secureSessionConfig = { secret: process.env.SESSION_SECRET || randomBytes(32).toString('hex'), resave: false, saveUninitialized: false, rolling: true, // Reset expiration on each request cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict' as const, maxAge: 24 * 60 * 60 * 1000, // 24 hours domain: process.env.COOKIE_DOMAIN, }, name: 'mcp.sid', // Custom session cookie name proxy: process.env.TRUST_PROXY === 'true', }; /** * CORS configuration for production */ export const corsConfig = { origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => { // Allow requests with no origin (mobile apps, Postman, etc.) if (!origin) { return callback(null, true); } // Check allowed origins from environment const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || []; if (process.env.NODE_ENV !== 'production') { // Allow all in development return callback(null, true); } if (allowedOrigins.includes(origin) || allowedOrigins.includes('*')) { return callback(null, true); } return callback(new Error('Not allowed by CORS')); }, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-CSRF-Token', 'X-Requested-With'], exposedHeaders: ['X-Total-Count', 'X-Page-Count'], maxAge: 600, // Cache preflight requests for 10 minutes preflightContinue: false, optionsSuccessStatus: 204, }; /** * Rate limiting configuration */ export const rateLimitConfig = { auth: { windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // 100 requests per window message: 'Too many authentication requests, please try again later', standardHeaders: true, legacyHeaders: false, skipSuccessfulRequests: false, skipFailedRequests: false, }, api: { windowMs: 60 * 1000, // 1 minute max: 60, // 60 requests per minute message: 'Too many requests, please slow down', standardHeaders: true, legacyHeaders: false, }, strict: { windowMs: 60 * 1000, // 1 minute max: 10, // 10 requests per minute (for sensitive endpoints) message: 'Rate limit exceeded', standardHeaders: true, legacyHeaders: false, }, }; /** * Security headers middleware (additional to Helmet) */ export function additionalSecurityHeaders(req: Request, res: Response, next: NextFunction): void { // Disable caching for sensitive data res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, private'); res.setHeader('Pragma', 'no-cache'); res.setHeader('Expires', '0'); // Additional security headers res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('X-XSS-Protection', '1; mode=block'); res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()'); next(); } /** * Remove sensitive headers from responses */ export function removeSensitiveHeaders(req: Request, res: Response, next: NextFunction): void { // Remove server identification headers res.removeHeader('X-Powered-By'); res.removeHeader('Server'); next(); } /** * Request sanitization middleware * Validates and sanitizes common attack vectors */ export function sanitizeRequest(req: Request, res: Response, next: NextFunction): void { // Check for suspicious patterns in URL const suspiciousPatterns = [ /\.\.\//, // Path traversal /<script/i, // XSS /union.*select/i, // SQL injection /javascript:/i, // JavaScript protocol ]; const url = req.url.toLowerCase(); const hashreat = suspiciousPatterns.some((pattern) => pattern.test(url)); if (hashreat) { console.warn(`[Security] Suspicious request detected: ${req.method} ${req.url} from ${req.ip}`); res.status(400).json({ error: 'Invalid request' }); return; } next(); } /** * IP whitelist middleware (optional) */ export function ipWhitelist(allowedIPs: string[]) { return (req: Request, res: Response, next: NextFunction): void => { const clientIP = req.ip || req.socket.remoteAddress || ''; if (!allowedIPs.includes(clientIP) && !allowedIPs.includes('*')) { console.warn(`[Security] IP ${clientIP} not in whitelist`); res.status(403).json({ error: 'Access denied' }); return; } next(); }; } /** * Certificate-based authentication middleware * Validates client certificates for mutual TLS */ export function certificateAuth(req: Request, res: Response, next: NextFunction): void { // Check if client certificate is present const cert = (req as any).socket.getPeerCertificate(); if (!cert || !cert.subject) { console.warn('[Security] No client certificate provided'); res.status(401).json({ error: 'Client certificate required' }); return; } // Validate certificate if (!(req as any).client.authorized) { console.warn('[Security] Invalid client certificate'); res.status(401).json({ error: 'Invalid client certificate' }); return; } // Extract certificate information (req as any).clientCert = { subject: cert.subject, issuer: cert.issuer, valid_from: cert.valid_from, valid_to: cert.valid_to, fingerprint: cert.fingerprint, }; console.error(`[Security] Client authenticated via certificate: ${cert.subject.CN}`); next(); } /** * Security configuration summary */ export function getSecurityConfig() { return { httpsEnforced: process.env.NODE_ENV === 'production', hstsEnabled: true, hstsMaxAge: 31536000, cspEnabled: true, cookieSecure: process.env.NODE_ENV === 'production', sessionTimeout: 24 * 60 * 60 * 1000, rateLimiting: true, corsRestricted: process.env.NODE_ENV === 'production', certificateAuth: process.env.ENABLE_CERT_AUTH === 'true', }; }

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/bradcstevens/copilot-studio-agent-direct-line-mcp'

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