Skip to main content
Glama

Watchtower DAP Windows Debugging

by rlaksana
redaction.ts10.8 kB
/** * Security Redaction System * * Handles automatic redaction of sensitive data in logs, command arguments, * environment variables, and user inputs following security-by-default principles. */ /** * Sensitive pattern definitions for redaction */ export const SENSITIVE_PATTERNS = { // API keys and tokens API_KEY: /(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer[_-]?token|secret[_-]?key)[\s=:]*['"]?([a-zA-Z0-9\-_]{10,})['"]?/gi, JWT_TOKEN: /eyJ[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]*/g, AWS_ACCESS_KEY: /AKIA[0-9A-Z]{16}/g, AWS_SECRET_KEY: /([0-9a-zA-Z\/+]{40})/g, // Database credentials DATABASE_URL: /(?:postgresql:\/\/|mysql:\/\/|mongodb:\/\/|redis:\/\/)[^:]+:[^@]+@[^\/]+\/[^\s]+/g, DB_PASSWORD: /(?:database[_-]?password|db[_-]?password)[\s=:]*['"]?([^'"\s]+)['"]?/gi, // User credentials EMAIL: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, PHONE: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, IP_ADDRESS: /\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g, // File paths with sensitive information WINDOWS_PATH: /C:\\Users\\[^\\]+\\AppData\\[A-Za-z0-9\\-_.]+/g, HOME_PATH: /\/home\/[^\/]+\/\.[A-Za-z0-9\-_]+/g, TEMP_PATH: /\/tmp\/[^\/]+/g, // Suffix patterns to redact SENSITIVE_SUFFIXES: [ 'password', 'passwd', 'pwd', 'secret', 'token', 'key', 'credential', 'auth', 'bearer', 'access', 'private', 'confidential', 'secret', ], }; /** * Redaction level enum */ export enum RedactionLevel { MINIMAL = 'minimal', MODERATE = 'moderate', AGGRESSIVE = 'aggressive', PARANOID = 'paranoid', } /** * Redaction configuration */ export interface RedactionConfig { level: RedactionLevel; customPatterns?: RegExp[]; customReplacements?: string; preserveLoggingContext?: boolean; } /** * Default redaction configuration */ export const DEFAULT_REDACTION_CONFIG: RedactionConfig = { level: RedactionLevel.MODERATE, customPatterns: [], customReplacements: '[REDACTED]', preserveLoggingContext: true, }; /** * Data Redactor class */ export class DataRedactor { private config: RedactionConfig; private compiledPatterns: RegExp[] = []; constructor(config: RedactionConfig = DEFAULT_REDACTION_CONFIG) { this.config = { ...DEFAULT_REDACTION_CONFIG, ...config }; this.compilePatterns(); } /** * Compile redaction patterns based on level */ private compilePatterns(): void { this.compiledPatterns = []; // Always include basic sensitive patterns this.compiledPatterns.push(SENSITIVE_PATTERNS.JWT_TOKEN); switch (this.config.level) { case RedactionLevel.MINIMAL: this.compiledPatterns.push(SENSITIVE_PATTERNS.API_KEY); break; case RedactionLevel.MODERATE: this.compiledPatterns.push( SENSITIVE_PATTERNS.API_KEY, SENSITIVE_PATTERNS.AWS_ACCESS_KEY, SENSITIVE_PATTERNS.DATABASE_URL, SENSITIVE_PATTERNS.DB_PASSWORD ); break; case RedactionLevel.AGGRESSIVE: this.compiledPatterns.push( SENSITIVE_PATTERNS.API_KEY, SENSITIVE_PATTERNS.JWT_TOKEN, SENSITIVE_PATTERNS.AWS_ACCESS_KEY, SENSITIVE_PATTERNS.AWS_SECRET_KEY, SENSITIVE_PATTERNS.DATABASE_URL, SENSITIVE_PATTERNS.DB_PASSWORD, SENSITIVE_PATTERNS.EMAIL, SENSITIVE_PATTERNS.PHONE, SENSITIVE_PATTERNS.WINDOWS_PATH, SENSITIVE_PATTERNS.HOME_PATH, SENSITIVE_PATTERNS.TEMP_PATH ); break; case RedactionLevel.PARANOID: // Include everything from AGGRESSIVE plus all suffix patterns this.compiledPatterns.push( SENSITIVE_PATTERNS.API_KEY, SENSITIVE_PATTERNS.JWT_TOKEN, SENSITIVE_PATTERNS.AWS_ACCESS_KEY, SENSITIVE_PATTERNS.AWS_SECRET_KEY, SENSITIVE_PATTERNS.DATABASE_URL, SENSITIVE_PATTERNS.DB_PASSWORD, SENSITIVE_PATTERNS.EMAIL, SENSITIVE_PATTERNS.PHONE, SENSITIVE_PATTERNS.IP_ADDRESS, SENSITIVE_PATTERNS.WINDOWS_PATH, SENSITIVE_PATTERNS.HOME_PATH, SENSITIVE_PATTERNS.TEMP_PATH ); // Add suffix patterns SENSITIVE_PATTERNS.SENSITIVE_SUFFIXES.forEach(suffix => { this.compiledPatterns.push( new RegExp(`${suffix}\\s*[=:]+\\s*['"]?([^'"\s]+)['"]?`, 'gi') ); }); break; } // Add custom patterns if (this.config.customPatterns) { this.compiledPatterns.push(...this.config.customPatterns); } } /** * Redact sensitive data from a string */ redact(input: string): string { if (!input || typeof input !== 'string') { return input; } let result = input; // Apply all redaction patterns this.compiledPatterns.forEach(pattern => { result = result.replace(pattern, this.config.customReplacements!); }); return result; } /** * Redact sensitive data from environment variables */ redactEnv(env: Record<string, string>): Record<string, string> { const redacted: Record<string, string> = {}; for (const [key, value] of Object.entries(env)) { const redactedKey = this.redactKey(key); const redactedValue = this.redact(value); if (redactedKey !== key || redactedValue !== value) { redacted[redactedKey] = redactedValue; } else { redacted[key] = value; } } return redacted; } /** * Redact sensitive data from command arguments */ redactArgs(args: string[]): string[] { return args.map(arg => this.redact(arg)); } /** * Redact sensitive data from an object */ redactObject<T extends Record<string, any>>(obj: T): Record<string, any> { const redacted: Record<string, any> = {}; for (const [key, value] of Object.entries(obj)) { const redactedKey = this.redactKey(key); if (typeof value === 'string') { redacted[redactedKey] = this.redact(value); } else if (typeof value === 'object' && value !== null) { if (Array.isArray(value)) { redacted[redactedKey] = value.map(item => { if (typeof item === 'string') { return this.redact(item); } return item; }); } else { redacted[redactedKey] = this.redactObject(value); } } else { redacted[redactedKey] = value; } } return redacted; } /** * Redact sensitive data from a log entry */ redactLogEntry(entry: Record<string, any>): Record<string, any> { const redacted = { ...entry }; // Redact message field if ((redacted as any).message) { (redacted as any).message = this.redact((redacted as any).message); } // Redact data field if ((redacted as any).data) { (redacted as any).data = this.redact((redacted as any).data); } // Redact any other string fields that might contain sensitive data for (const key in redacted) { if (typeof redacted[key] === 'string' && key !== 'timestamp' && key !== 'level') { redacted[key] = this.redact(redacted[key]); } } return redacted; } /** * Redact a key name if it's sensitive */ private redactKey(key: string): string { // Check if key contains sensitive suffix const lowerKey = key.toLowerCase(); const isSensitive = SENSITIVE_PATTERNS.SENSITIVE_SUFFIXES.some(suffix => lowerKey.includes(suffix) ); if (isSensitive) { return '[SENSITIVE_KEY]'; } return key; } /** * Update redaction configuration */ updateConfig(newConfig: Partial<RedactionConfig>): void { this.config = { ...this.config, ...newConfig }; this.compilePatterns(); } /** * Get current redaction patterns count */ getPatternCount(): number { return this.compiledPatterns.length; } /** * Add custom redaction pattern */ addPattern(pattern: RegExp, replacement?: string): void { this.config.customPatterns = this.config.customPatterns || []; this.config.customPatterns.push(pattern); if (replacement) { this.config.customReplacements = replacement; } this.compilePatterns(); } /** * Remove custom redaction pattern */ removePattern(pattern: RegExp): void { if (this.config.customPatterns) { this.config.customPatterns = this.config.customPatterns.filter(p => p !== pattern); this.compilePatterns(); } } /** * Check if string contains sensitive patterns (without redacting) */ hasSensitiveData(input: string): boolean { if (!input || typeof input !== 'string') { return false; } return this.compiledPatterns.some(pattern => pattern.test(input)); } /** * Get redaction summary */ getSummary(): { level: RedactionLevel; patternCount: number; patterns: string[]; } { return { level: this.config.level, patternCount: this.compiledPatterns.length, patterns: this.compiledPatterns.map(p => p.toString()), }; } } /** * Global redactor instance */ let globalRedactor: DataRedactor | null = null; /** * Get or create global redactor instance */ export function getGlobalRedactor(): DataRedactor { if (!globalRedactor) { globalRedactor = new DataRedactor(); } return globalRedactor; } /** * Set global redactor configuration */ export function setGlobalRedactorConfig(config: RedactionConfig): void { const redactor = getGlobalRedactor(); redactor.updateConfig(config); } /** * Convenience functions for redaction */ export function redact(input: string): string { return getGlobalRedactor().redact(input); } export function redactEnv(env: Record<string, string>): Record<string, string> { return getGlobalRedactor().redactEnv(env); } export function redactArgs(args: string[]): string[] { return getGlobalRedactor().redactArgs(args); } export function redactObject<T extends Record<string, any>>(obj: T): Record<string, any> { return getGlobalRedactor().redactObject(obj); } export function redactLogEntry(entry: Record<string, any>): Record<string, any> { return getGlobalRedactor().redactLogEntry(entry); } /** * Create redactor with custom configuration */ export function createRedactor(config: RedactionConfig): DataRedactor { return new DataRedactor(config); } /** * Validate redaction level */ export function validateRedactionLevel(level: string): RedactionLevel | null { const levels = Object.values(RedactionLevel); return levels.includes(level as RedactionLevel) ? (level as RedactionLevel) : null; }

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/rlaksana/mcp-watchtower'

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