Skip to main content
Glama

Claude MCP Server Integration

by mokemoke0821
security.ts5.91 kB
import DOMPurify from 'dompurify' import validator from 'validator' // 🔒 入力検証とサニタイゼーション export class SecurityUtils { // 📝 HTMLサニタイゼーション (XSS対策) static sanitizeHtml(input: string): string { return DOMPurify.sanitize(input, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'br', 'p', 'span'], ALLOWED_ATTR: ['class', 'style'], FORBID_SCRIPTS: true, FORBID_TAGS: ['script', 'object', 'embed', 'link', 'style', 'img', 'svg'], STRIP_COMMENTS: true }) } // 🔍 入力文字列の基本検証 static validateInput(input: unknown): input is string { return typeof input === 'string' && input.length > 0 && input.length <= 10000 } // 🚫 SQLインジェクション対策 static sanitizeSQLInput(input: string): string { // 危険な文字をエスケープ return input .replace(/['";\\]/g, '') .replace(/(--)|(\/\*)|(\*\/)/g, '') .replace(/(union|select|insert|update|delete|drop|create|alter|execute|script)/gi, '') .trim() } // 📧 Email検証 static validateEmail(email: string): boolean { return validator.isEmail(email) && email.length <= 320 } // 🔐 パスワード強度チェック static validatePassword(password: string): { isValid: boolean errors: string[] } { const errors: string[] = [] if (password.length < 8) { errors.push('パスワードは8文字以上である必要があります') } if (!/[A-Z]/.test(password)) { errors.push('大文字を含める必要があります') } if (!/[a-z]/.test(password)) { errors.push('小文字を含める必要があります') } if (!/[0-9]/.test(password)) { errors.push('数字を含める必要があります') } if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { errors.push('特殊文字を含める必要があります') } return { isValid: errors.length === 0, errors } } // 🌐 URL検証 static validateUrl(url: string): boolean { try { const parsedUrl = new URL(url) return ['http:', 'https:'].includes(parsedUrl.protocol) && validator.isURL(url, { protocols: ['http', 'https'], require_protocol: true, require_host: true, require_valid_protocol: true }) } catch { return false } } // 🔢 数値検証 static validateNumber(value: unknown, min?: number, max?: number): value is number { if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) { return false } if (min !== undefined && value < min) { return false } if (max !== undefined && value > max) { return false } return true } // 🆔 UUID検証 static validateUUID(uuid: string): boolean { return validator.isUUID(uuid, 4) } // 📱 JSON検証とサニタイゼーション static sanitizeJSON(input: string): object | null { try { const parsed = JSON.parse(input) // オブジェクトの深さ制限(DoS攻撃対策) if (this.getObjectDepth(parsed) > 10) { throw new Error('Object too deep') } // 文字列プロパティのサニタイゼーション return this.sanitizeObjectStrings(parsed) } catch { return null } } // 🏗️ オブジェクト深度計算 private static getObjectDepth(obj: any, depth = 0): number { if (depth > 10) return depth // 制限値 if (obj === null || typeof obj !== 'object') { return depth } let maxDepth = depth for (const key in obj) { if (obj.hasOwnProperty(key)) { const currentDepth = this.getObjectDepth(obj[key], depth + 1) maxDepth = Math.max(maxDepth, currentDepth) } } return maxDepth } // 🧹 オブジェクト内文字列サニタイゼーション private static sanitizeObjectStrings(obj: any): any { if (typeof obj === 'string') { return this.sanitizeHtml(obj) } if (Array.isArray(obj)) { return obj.map(item => this.sanitizeObjectStrings(item)) } if (obj !== null && typeof obj === 'object') { const sanitized: any = {} for (const key in obj) { if (obj.hasOwnProperty(key)) { sanitized[key] = this.sanitizeObjectStrings(obj[key]) } } return sanitized } return obj } // ⏱️ レート制限チェック(簡易版) private static requestTimestamps = new Map<string, number[]>() static checkRateLimit(identifier: string, maxRequests = 100, windowMs = 60000): boolean { const now = Date.now() const windowStart = now - windowMs if (!this.requestTimestamps.has(identifier)) { this.requestTimestamps.set(identifier, []) } const timestamps = this.requestTimestamps.get(identifier)! // 古いタイムスタンプを削除 const recentTimestamps = timestamps.filter(timestamp => timestamp > windowStart) if (recentTimestamps.length >= maxRequests) { return false // レート制限に達している } recentTimestamps.push(now) this.requestTimestamps.set(identifier, recentTimestamps) return true } // 🛡️ CSP (Content Security Policy) ヘルパー static generateCSPNonce(): string { const array = new Uint8Array(16) crypto.getRandomValues(array) return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('') } // 🔐 セッション管理 static validateSession(sessionId: string): boolean { return this.validateUUID(sessionId) && sessionId.length === 36 } } // 🎯 型定義エクスポート export interface SecurityValidationResult { isValid: boolean errors?: string[] sanitizedValue?: any } export interface RateLimitConfig { maxRequests: number windowMs: number identifier: string }

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/mokemoke0821/claude-mcp-integration'

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