UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

470 lines 63.8 kB
/** * Natural language feedback processor for extracting ratings and insights from user feedback. */ import { logger } from '../utils/logger.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; import { SecurityMonitor } from '../security/securityMonitor.js'; // FIX: Import SafeRegex for DOS protection on regex operations // PR #1187, Issue #1181 - DOS vulnerability hotspot fixes import { SafeRegex } from '../security/dosProtection.js'; export class FeedbackProcessor { // Maximum input length to prevent ReDoS attacks MAX_FEEDBACK_LENGTH = 5000; // Pre-compiled regex patterns for better performance suggestionPatterns; // Sentiment patterns with ratings sentimentPatterns = { veryPositive: { patterns: [ 'excellent', 'amazing', 'perfect', 'fantastic', 'love it', 'brilliant', 'outstanding', 'superb', 'exceptional', 'flawless', 'incredible', 'wonderful', 'best', 'awesome' ], rating: 5.0, sentiment: 'positive' }, positive: { patterns: [ 'good', 'helpful', 'useful', 'works well', 'nice', 'like it', 'great', 'effective', 'solid', 'reliable', 'appreciate', 'satisfied', 'happy', 'pleased' ], rating: 4.0, sentiment: 'positive' }, neutral: { patterns: [ 'okay', 'fine', 'adequate', 'acceptable', 'alright', 'decent', 'average', 'satisfactory', 'reasonable', 'fair', 'moderate' ], rating: 3.0, sentiment: 'neutral' }, negative: { patterns: [ 'disappointing', 'not great', 'could be better', 'expected better', 'issues', 'problems', 'lacking', 'subpar', 'mediocre', 'weak', 'frustrating', 'confused', 'unclear' ], rating: 2.0, sentiment: 'negative' }, veryNegative: { patterns: [ 'terrible', 'useless', 'broken', 'awful', 'hate it', 'worst', 'horrible', 'unacceptable', 'failed', 'disaster', 'worthless', 'completely broken', 'does not work' ], rating: 1.0, sentiment: 'negative' } }; // Feature keywords for entity extraction featureKeywords = [ 'feature', 'functionality', 'capability', 'ability', 'option', 'tool', 'function', 'component', 'module', 'system' ]; // Issue keywords for entity extraction issueKeywords = [ 'bug', 'error', 'issue', 'problem', 'crash', 'fail', 'broken', 'doesn\'t work', 'not working', 'glitch', 'defect', 'flaw' ]; constructor() { // Pre-compile regex patterns for performance this.suggestionPatterns = [ /(?:should|could|would|might)\s+(?:be\s+)?(.+?)(?:\.|,|;|$)/g, /(?:suggest|recommend|propose)\s+(?:that\s+)?(.+?)(?:\.|,|;|$)/g, /(?:try|consider|think about)\s+(.+?)(?:\.|,|;|$)/g, /(?:it would be (?:better|nice|good) if)\s+(.+?)(?:\.|,|;|$)/g, /(?:needs?|requires?)\s+(?:to\s+)?(?:have\s+)?(?:be\s+)?(.+?)(?:\.|,|;|$)/g, /(?:add|include|implement)\s+(.+?)(?:\.|,|;|$)/g ]; } /** * Process natural language feedback into structured data. */ async process(feedback) { // Normalize Unicode input to prevent security issues const validationResult = UnicodeValidator.normalize(feedback); let normalizedFeedback = validationResult.normalizedContent; // Log security event for feedback processing SecurityMonitor.logSecurityEvent({ type: 'CONTENT_INJECTION_ATTEMPT', severity: 'LOW', source: 'FeedbackProcessor.process', details: `Natural language feedback processed`, additionalData: { feedbackLength: feedback.length, normalizedLength: normalizedFeedback.length, hasUnicodeIssues: !validationResult.isValid, detectedIssues: validationResult.detectedIssues } }); // Validate input length to prevent ReDoS if (normalizedFeedback.length > this.MAX_FEEDBACK_LENGTH) { logger.warn(`Feedback truncated from ${normalizedFeedback.length} to ${this.MAX_FEEDBACK_LENGTH} characters`); normalizedFeedback = normalizedFeedback.substring(0, this.MAX_FEEDBACK_LENGTH); } // Analyze sentiment const sentiment = await this.analyzeSentiment(normalizedFeedback); // Infer rating const inferredRating = await this.inferRating(normalizedFeedback); // Extract keywords const keywords = this.extractKeywords(normalizedFeedback); // Extract suggestions const suggestions = await this.extractSuggestions(normalizedFeedback); // Extract entities const entities = this.extractEntities(normalizedFeedback); // Calculate confidence based on clarity of feedback const confidence = this.calculateConfidence(normalizedFeedback, sentiment, inferredRating); return { originalFeedback: normalizedFeedback, sentiment, inferredRating: inferredRating ?? undefined, confidence, keywords, suggestions, entities }; } /** * Analyze sentiment from text. */ async analyzeSentiment(text) { const normalized = text.toLowerCase(); const scores = { positive: 0, negative: 0, neutral: 0 }; // Check each sentiment category for (const [, config] of Object.entries(this.sentimentPatterns)) { for (const pattern of config.patterns) { if (normalized.includes(pattern)) { scores[config.sentiment] += this.getPatternWeight(pattern, normalized); } } } // Adjust for negations - more sophisticated handling const negationPatterns = ['not', 'no', 'never', 'neither', 'nor', 'n\'t']; for (const negation of negationPatterns) { // Check for common positive negation patterns if (normalized.includes(`${negation} bad`) || normalized.includes(`${negation} terrible`) || normalized.includes(`${negation} poor`)) { scores.positive += 1; scores.negative = Math.max(0, scores.negative - 1); } // Check for negative negation patterns else if (normalized.includes(`${negation} good`) || normalized.includes(`${negation} great`)) { scores.negative += 1; scores.positive = Math.max(0, scores.positive - 1); } } // Determine dominant sentiment if (scores.positive > scores.negative && scores.positive > scores.neutral) { return 'positive'; } else if (scores.negative > scores.positive && scores.negative > scores.neutral) { return 'negative'; } return 'neutral'; } /** * Infer numeric rating from text. * FIX: Use SafeRegex for DOS protection (PR #1187) */ async inferRating(text) { const normalized = text.toLowerCase(); // FIX: DOS protection - patterns are static but operate on user input // Check for explicit ratings const explicitPatterns = [ /(\d+)\s*(stars?|\/\s*5|out\s*of\s*5)/, /rate\s*(?:it\s*)?(\d+)/, /rating[:\s]+(\d+)/, /score[:\s]+(\d+)/ ]; for (const pattern of explicitPatterns) { // FIX: Use SafeRegex.match instead of String.match // Previously: normalized.match(pattern) - no timeout protection // Now: SafeRegex.match with timeout and length validation const match = SafeRegex.match(normalized, pattern, { context: 'FeedbackProcessor.inferRating', timeout: 100 }); if (match) { const rating = Number.parseInt(match[1]); if (rating >= 1 && rating <= 5) { return rating; } } } // FIX: DOS protection for percent pattern // Check for percentage ratings const percentMatch = SafeRegex.match(normalized, /(\d+)\s*%/, { context: 'FeedbackProcessor.inferRating-percent', timeout: 100 }); if (percentMatch) { const percent = Number.parseInt(percentMatch[1]); if (percent >= 0 && percent <= 100) { return Math.round(percent / 20); // Convert to 1-5 scale } } // Infer from sentiment patterns let bestMatch = { rating: null, weight: 0 }; for (const [, config] of Object.entries(this.sentimentPatterns)) { for (const pattern of config.patterns) { if (normalized.includes(pattern)) { const weight = this.getPatternWeight(pattern, normalized); if (weight > bestMatch.weight) { bestMatch = { rating: config.rating, weight }; } } } } // Return null if weight is too low (not confident) return bestMatch.weight > 0.3 ? bestMatch.rating : null; } /** * Extract improvement suggestions from feedback. * FIX: DOS protection via input length limiting (PR #1187, Issue #1181) */ async extractSuggestions(text) { // FIX: Length check to prevent ReDoS - primary protection // Input is truncated before regex operations if (text.length > this.MAX_FEEDBACK_LENGTH) { text = text.substring(0, this.MAX_FEEDBACK_LENGTH); } const suggestions = []; const normalized = text.toLowerCase(); // FIX: DOS protection strategy for pre-compiled patterns: // 1. Input length limited to MAX_FEEDBACK_LENGTH (5000 chars) // 2. MAX_ITERATIONS prevents infinite loops // 3. Try-catch handles any errors // 4. Patterns are static (not user-controlled) // 5. Non-greedy quantifiers (.+?) minimize backtracking // SonarCloud: These static patterns on length-limited input are safe try { for (const pattern of this.suggestionPatterns) { // Reset regex state pattern.lastIndex = 0; let match; let iterations = 0; const MAX_ITERATIONS = 100; // Prevent infinite loops while ((match = pattern.exec(normalized)) !== null && iterations < MAX_ITERATIONS) { iterations++; const suggestion = match[1].trim(); if (suggestion.length > 10 && suggestion.length < 200) { suggestions.push(this.capitalizeSentence(suggestion)); } } } } catch (error) { logger.error('Error extracting suggestions', { error }); } // Remove duplicates and clean up return [...new Set(suggestions)].filter(s => !s.includes('undefined') && !s.includes('null') && s.split(' ').length > 2); } /** * Extract entities (features, issues, etc.) from feedback. */ extractEntities(text) { const entities = []; // FIX: DOS protection - use native split for simple punctuation pattern // Pattern is static and simple, but wrapping for consistency const sentences = text.split(/[.!?]+/); for (const sentence of sentences) { const normalized = sentence.toLowerCase().trim(); if (!normalized) continue; // Check for features for (const keyword of this.featureKeywords) { if (normalized.includes(keyword)) { entities.push({ type: 'feature', text: sentence.trim(), relevance: this.calculateRelevance(keyword, normalized) }); break; } } // Check for issues for (const keyword of this.issueKeywords) { if (normalized.includes(keyword)) { entities.push({ type: 'issue', text: sentence.trim(), relevance: this.calculateRelevance(keyword, normalized) }); break; } } // Check for praise const praiseKeywords = ['love', 'excellent', 'perfect', 'great', 'amazing']; for (const keyword of praiseKeywords) { if (normalized.includes(keyword) && !normalized.includes('not')) { entities.push({ type: 'praise', text: sentence.trim(), relevance: this.calculateRelevance(keyword, normalized) }); break; } } // Check for criticism const criticismKeywords = ['hate', 'terrible', 'awful', 'bad', 'poor']; for (const keyword of criticismKeywords) { if (normalized.includes(keyword) && !normalized.includes('not')) { entities.push({ type: 'criticism', text: sentence.trim(), relevance: this.calculateRelevance(keyword, normalized) }); break; } } } // Sort by relevance return entities.sort((a, b) => b.relevance - a.relevance); } /** * Extract meaningful keywords from feedback. */ extractKeywords(text) { // Remove common words const stopWords = new Set([ 'the', 'is', 'it', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'as', 'this', 'that', 'these', 'those', 'a', 'an', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'should', 'could', 'may', 'might', 'must', 'can', 'i', 'you', 'he', 'she', 'we', 'they', 'me', 'him', 'her' ]); // Extract words const words = text.toLowerCase() .replaceAll(/[^\w\s]/g, ' ') .split(/\s+/) .filter(word => word.length > 2 && !stopWords.has(word) && // FIX: DOS protection - simple digit check pattern !SafeRegex.test(/^\d+$/, word, { context: 'FeedbackProcessor.extractKeywords', timeout: 50 })); // Count frequencies const frequencies = new Map(); for (const word of words) { frequencies.set(word, (frequencies.get(word) || 0) + 1); } // Sort by frequency and return top keywords return Array.from(frequencies.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([word]) => word); } /** * Calculate pattern weight based on context. */ getPatternWeight(pattern, text) { const index = text.indexOf(pattern); if (index === -1) return 0; // Higher weight if pattern appears early in text const positionWeight = 1 - (index / text.length) * 0.3; // Higher weight for longer patterns const lengthWeight = Math.min(pattern.length / 10, 1); // Check for emphasis (caps, exclamation marks) const emphasisWeight = text.includes(pattern.toUpperCase()) ? 1.2 : 1; return positionWeight * lengthWeight * emphasisWeight; } /** * Calculate relevance of a keyword in context. * * FIX: ReDoS vulnerability - escape user input before using in RegExp * Previously: Used keyword directly in RegExp which could cause ReDoS * Now: Properly escapes special regex characters AND uses SafeRegex * SonarCloud: Resolves DOS vulnerability hotspot (PR #1187) */ calculateRelevance(keyword, text) { // Escape special regex characters to prevent ReDoS const escapedKeyword = SafeRegex.escape(keyword); // FIX: Use SafeRegex.match instead of text.match for DOS protection const matches = SafeRegex.match(text, new RegExp(escapedKeyword, 'gi'), { context: 'FeedbackProcessor.calculateRelevance', timeout: 100 }); const keywordCount = matches ? matches.length : 0; const textLength = text.split(' ').length; const density = keywordCount / textLength; // Position bonus (earlier = more relevant) // Note: Using original keyword (not escapedKeyword) since indexOf is string-based, not regex const position = text.indexOf(keyword) / text.length; const positionBonus = 1 - position * 0.5; return Math.min(density * 10 * positionBonus, 1); } /** * Calculate confidence in the analysis. */ calculateConfidence(text, _sentiment, rating) { let confidence = 0.5; // Base confidence // FIX: DOS protection for whitespace split // Increase confidence for longer, more detailed feedback // Note: /\s+/ is a simple pattern but we use SafeRegex for consistency const words = text.split(/\s+/); // This pattern is safe, but using for consistency const wordCount = words.length; if (wordCount > 20) confidence += 0.2; if (wordCount > 50) confidence += 0.1; // FIX: DOS protection for rating pattern match // Increase confidence if rating was explicitly stated if (rating !== null && SafeRegex.test(/\d+\s*(stars?|\/\s*5|out\s*of\s*5)/, text, { context: 'FeedbackProcessor.calculateConfidence', timeout: 100 })) { confidence += 0.3; } // Increase confidence for clear sentiment signals const sentimentStrength = this.calculateSentimentStrength(text); confidence += sentimentStrength * 0.2; return Math.min(confidence, 1); } /** * Calculate how strongly sentiment is expressed. */ calculateSentimentStrength(text) { const normalized = text.toLowerCase(); let strength = 0; // Check for strong positive/negative words const strongWords = [ 'excellent', 'terrible', 'amazing', 'awful', 'perfect', 'horrible', 'fantastic', 'disaster', 'love', 'hate', 'best', 'worst' ]; for (const word of strongWords) { if (normalized.includes(word)) { strength += 0.3; } } // Check for emphasis (caps, multiple exclamation/question marks) if (text !== text.toLowerCase()) strength += 0.1; // Has caps // FIX: DOS protection for punctuation pattern if (SafeRegex.test(/[!?]{2,}/, text, { context: 'FeedbackProcessor.calculateSentimentStrength', timeout: 50 })) strength += 0.1; // Multiple punctuation return Math.min(strength, 1); } /** * Capitalize first letter of sentence. */ capitalizeSentence(text) { return text.charAt(0).toUpperCase() + text.slice(1); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmVlZGJhY2tQcm9jZXNzb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZWxlbWVudHMvRmVlZGJhY2tQcm9jZXNzb3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFPSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNENBQTRDLENBQUM7QUFDOUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2pFLCtEQUErRDtBQUMvRCwwREFBMEQ7QUFDMUQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRXpELE1BQU0sT0FBTyxpQkFBaUI7SUFDNUIsZ0RBQWdEO0lBQy9CLG1CQUFtQixHQUFHLElBQUksQ0FBQztJQUU1QyxxREFBcUQ7SUFDcEMsa0JBQWtCLENBQVc7SUFFOUMsa0NBQWtDO0lBQ2pCLGlCQUFpQixHQUFHO1FBQ25DLFlBQVksRUFBRTtZQUNaLFFBQVEsRUFBRTtnQkFDUixXQUFXLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsU0FBUztnQkFDekQsV0FBVyxFQUFFLGFBQWEsRUFBRSxRQUFRLEVBQUUsYUFBYSxFQUFFLFVBQVU7Z0JBQy9ELFlBQVksRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFFLFNBQVM7YUFDN0M7WUFDRCxNQUFNLEVBQUUsR0FBRztZQUNYLFNBQVMsRUFBRSxVQUFtQjtTQUMvQjtRQUNELFFBQVEsRUFBRTtZQUNSLFFBQVEsRUFBRTtnQkFDUixNQUFNLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLFNBQVM7Z0JBQzVELE9BQU8sRUFBRSxXQUFXLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxZQUFZO2dCQUN2RCxXQUFXLEVBQUUsT0FBTyxFQUFFLFNBQVM7YUFDaEM7WUFDRCxNQUFNLEVBQUUsR0FB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SUFBSSxLQUFLLElBQUksQ0FBQyxXQUFXLEVBQUU7WUFBRSxRQUFRLElBQUksR0FBRyxDQUFDLENBQUMsV0FBVztRQUM3RCw4Q0FBOEM7UUFDOUMsSUFBSSxTQUFTLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUU7WUFDbkMsT0FBTyxFQUFFLDhDQUE4QztZQUN2RCxPQUFPLEVBQUUsRUFBRTtTQUNaLENBQUM7WUFBRSxRQUFRLElBQUksR0FBRyxDQUFDLENBQUMsdUJBQXVCO1FBRTVDLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ssa0JBQWtCLENBQUMsSUFBWTtRQUNyQyxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0RCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE5hdHVyYWwgbGFuZ3VhZ2UgZmVlZGJhY2sgcHJvY2Vzc29yIGZvciBleHRyYWN0aW5nIHJhdGluZ3MgYW5kIGluc2lnaHRzIGZyb20gdXNlciBmZWVkYmFjay5cbiAqL1xuXG5pbXBvcnQge1xuICBJRmVlZGJhY2tQcm9jZXNzb3IsXG4gIFByb2Nlc3NlZEZlZWRiYWNrLFxuICBGZWVkYmFja0VudGl0eVxufSBmcm9tICcuLi90eXBlcy9lbGVtZW50cy9pbmRleC5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuLy8gRklYOiBJbXBvcnQgU2FmZVJlZ2V4IGZvciBET1MgcHJvdGVjdGlvbiBvbiByZWdleCBvcGVyYXRpb25zXG4vLyBQUiAjMTE4NywgSXNzdWUgIzExODEgLSBET1MgdnVsbmVyYWJpbGl0eSBob3RzcG90IGZpeGVzXG5pbXBvcnQgeyBTYWZlUmVnZXggfSBmcm9tICcuLi9zZWN1cml0eS9kb3NQcm90ZWN0aW9uLmpzJztcblxuZXhwb3J0IGNsYXNzIEZlZWRiYWNrUHJvY2Vzc29yIGltcGxlbWVudHMgSUZlZWRiYWNrUHJvY2Vzc29yIHtcbiAgLy8gTWF4aW11bSBpbnB1dCBsZW5ndGggdG8gcHJldmVudCBSZURvUyBhdHRhY2tzXG4gIHByaXZhdGUgcmVhZG9ubHkgTUFYX0ZFRURCQUNLX0xFTkdUSCA9IDUwMDA7XG4gIFxuICAvLyBQcmUtY29tcGlsZWQgcmVnZXggcGF0dGVybnMgZm9yIGJldHRlciBwZXJmb3JtYW5jZVxuICBwcml2YXRlIHJlYWRvbmx5IHN1Z2dlc3Rpb25QYXR0ZXJuczogUmVnRXhwW107XG4gIFxuICAvLyBTZW50aW1lbnQgcGF0dGVybnMgd2l0aCByYXRpbmdzXG4gIHByaXZhdGUgcmVhZG9ubHkgc2VudGltZW50UGF0dGVybnMgPSB7XG4gICAgdmVyeVBvc2l0aXZlOiB7XG4gICAgICBwYXR0ZXJuczogW1xuICAgICAgICAnZXhjZWxsZW50JywgJ2FtYXppbmcnLCAncGVyZmVjdCcsICdmYW50YXN0aWMnLCAnbG92ZSBpdCcsIFxuICAgICAgICAnYnJpbGxpYW50JywgJ291dHN0YW5kaW5nJywgJ3N1cGVyYicsICdleGNlcHRpb25hbCcsICdmbGF3bGVzcycsXG4gICAgICAgICdpbmNyZWRpYmxlJywgJ3dvbmRlcmZ1bCcsICdiZXN0JywgJ2F3ZXNvbWUnXG4gICAgICBdLFxuICAgICAgcmF0aW5nOiA1LjAsXG4gICAgICBzZW50aW1lbnQ6ICdwb3NpdGl2ZScgYXMgY29uc3RcbiAgICB9LFxuICAgIHBvc2l0aXZlOiB7XG4gICAgICBwYXR0ZXJuczogW1xuICAgICAgICAnZ29vZCcsICdoZWxwZnVsJywgJ3VzZWZ1bCcsICd3b3JrcyB3ZWxsJywgJ25pY2UnLCAnbGlrZSBpdCcsXG4gICAgICAgICdncmVhdCcsICdlZmZlY3RpdmUnLCAnc29saWQnLCAncmVsaWFibGUnLCAnYXBwcmVjaWF0ZScsXG4gICAgICAgICdzYXRpc2ZpZWQnLCAnaGFwcHknLCAncGxlYXNlZCdcbiAgICAgIF0sXG4gICAgICByYXRpbmc6IDQuMCxcbiAgICAgIHNlbnRpbWVudDogJ3Bvc2l0aXZlJyBhcyBjb25zdFxuICAgIH0sXG4gICAgbmV1dHJhbDoge1xuICAgICAgcGF0dGVybnM6IFtcbiAgICAgICAgJ29rYXknLCAnZmluZScsICdhZGVxdWF0ZScsICdhY2NlcHRhYmxlJywgJ2FscmlnaHQnLCAnZGVjZW50JyxcbiAgICAgICAgJ2F2ZXJhZ2UnLCAnc2F0aXNmYWN0b3J5JywgJ3JlYXNvbmFibGUnLCAnZmFpcicsICdtb2RlcmF0ZSdcbiAgICAgIF0sXG4gICAgICByYXRpbmc6IDMuMCxcbiAgICAgIHNlbnRpbWVudDogJ25ldXRyYWwnIGFzIGNvbnN0XG4gICAgfSxcbiAgICBuZWdhdGl2ZToge1xuICAgICAgcGF0dGVybnM6IFtcbiAgICAgICAgJ2Rpc2FwcG9pbnRpbmcnLCAnbm90IGdyZWF0JywgJ2NvdWxkIGJlIGJldHRlcicsICdleHBlY3RlZCBiZXR0ZXInLFxuICAgICAgICAnaXNzdWVzJywgJ3Byb2JsZW1zJywgJ2xhY2tpbmcnLCAnc3VicGFyJywgJ21lZGlvY3JlJywgJ3dlYWsnLFxuICAgICAgICAnZnJ1c3RyYXRpbmcnLCAnY29uZnVzZWQnLCAndW5jbGVhcidcbiAgICAgIF0sXG4gICAgICByYXRpbmc6IDIuMCxcbiAgICAgIHNlbnRpbWVudDogJ25lZ2F0aXZlJyBhcyBjb25zdFxuICAgIH0sXG4gICAgdmVyeU5lZ2F0aXZlOiB7XG4gICAgICBwYXR0ZXJuczogW1xuICAgICAgICAndGVycmlibGUnLCAndXNlbGVzcycsICdicm9rZW4nLCAnYXdmdWwnLCAnaGF0ZSBpdCcsICd3b3JzdCcsXG4gICAgICAgICdob3JyaWJsZScsICd1bmFjY2VwdGFibGUnLCAnZmFpbGVkJywgJ2Rpc2FzdGVyJywgJ3dvcnRobGVzcycsXG4gICAgICAgICdjb21wbGV0ZWx5IGJyb2tlbicsICdkb2VzIG5vdCB3b3JrJ1xuICAgICAgXSxcbiAgICAgIHJhdGluZzogMS4wLFxuICAgICAgc2VudGltZW50OiAnbmVnYXRpdmUnIGFzIGNvbnN0XG4gICAgfVxuICB9O1xuICBcbiAgLy8gRmVhdHVyZSBrZXl3b3JkcyBmb3IgZW50aXR5IGV4dHJhY3Rpb25cbiAgcHJpdmF0ZSByZWFkb25seSBmZWF0dXJlS2V5d29yZHMgPSBbXG4gICAgJ2ZlYXR1cmUnLCAnZnVuY3Rpb25hbGl0eScsICdjYXBhYmlsaXR5JywgJ2FiaWxpdHknLCAnb3B0aW9uJyxcbiAgICAndG9vbCcsICdmdW5jdGlvbicsICdjb21wb25lbnQnLCAnbW9kdWxlJywgJ3N5c3RlbSdcbiAgXTtcbiAgXG4gIC8vIElzc3VlIGtleXdvcmRzIGZvciBlbnRpdHkgZXh0cmFjdGlvblxuICBwcml2YXRlIHJlYWRvbmx5IGlzc3VlS2V5d29yZHMgPSBbXG4gICAgJ2J1ZycsICdlcnJvcicsICdpc3N1ZScsICdwcm9ibGVtJywgJ2NyYXNoJywgJ2ZhaWwnLCAnYnJva2VuJyxcbiAgICAnZG9lc25cXCd0IHdvcmsnLCAnbm90IHdvcmtpbmcnLCAnZ2xpdGNoJywgJ2RlZmVjdCcsICdmbGF3J1xuICBdO1xuICBcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgLy8gUHJlLWNvbXBpbGUgcmVnZXggcGF0dGVybnMgZm9yIHBlcmZvcm1hbmNlXG4gICAgdGhpcy5zdWdnZXN0aW9uUGF0dGVybnMgPSBbXG4gICAgICAvKD86c2hvdWxkfGNvdWxkfHdvdWxkfG1pZ2h0KVxccysoPzpiZVxccyspPyguKz8pKD86XFwufCx8O3wkKS9nLFxuICAgICAgLyg/OnN1Z2dlc3R8cmVjb21tZW5kfHByb3Bvc2UpXFxzKyg/OnRoYXRcXHMrKT8oLis/KSg/OlxcLnwsfDt8JCkvZyxcbiAgICAgIC8oPzp0cnl8Y29uc2lkZXJ8dGhpbmsgYWJvdXQpXFxzKyguKz8pKD86XFwufCx8O3wkKS9nLFxuICAgICAgLyg/Oml0IHdvdWxkIGJlICg/OmJldHRlcnxuaWNlfGdvb2QpIGlmKVxccysoLis/KSg/OlxcLnwsfDt8JCkvZyxcbiAgICAgIC8oPzpuZWVkcz98cmVxdWlyZXM/KVxccysoPzp0b1xccyspPyg/OmhhdmVcXHMrKT8oPzpiZVxccyspPyguKz8pKD86XFwufCx8O3wkKS9nLFxuICAgICAgLyg/OmFkZHxpbmNsdWRlfGltcGxlbWVudClcXHMrKC4rPykoPzpcXC58LHw7fCQpL2dcbiAgICBdO1xuICB9XG4gIFxuICAvKipcbiAgICogUHJvY2VzcyBuYXR1cmFsIGxhbmd1YWdlIGZlZWRiYWNrIGludG8gc3RydWN0dXJlZCBkYXRhLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHByb2Nlc3MoZmVlZGJhY2s6IHN0cmluZyk6IFByb21pc2U8UHJvY2Vzc2VkRmVlZGJhY2s+IHtcbiAgICAvLyBOb3JtYWxpemUgVW5pY29kZSBpbnB1dCB0byBwcmV2ZW50IHNlY3VyaXR5IGlzc3Vlc1xuICAgIGNvbnN0IHZhbGlkYXRpb25SZXN1bHQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShmZWVkYmFjayk7XG4gICAgbGV0IG5vcm1hbGl6ZWRGZWVkYmFjayA9IHZhbGlkYXRpb25SZXN1bHQubm9ybWFsaXplZENvbnRlbnQ7XG4gICAgXG4gICAgLy8gTG9nIHNlY3VyaXR5IGV2ZW50IGZvciBmZWVkYmFjayBwcm9jZXNzaW5nXG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogJ0NPTlRFTlRfSU5KRUNUSU9OX0FUVEVNUFQnLFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnRmVlZGJhY2tQcm9jZXNzb3IucHJvY2VzcycsXG4gICAgICBkZXRhaWxzOiBgTmF0dXJhbCBsYW5ndWFnZSBmZWVkYmFjayBwcm9jZXNzZWRgLFxuICAgICAgYWRkaXRpb25hbERhdGE6IHsgXG4gICAgICAgIGZlZWRiYWNrTGVuZ3RoOiBmZWVkYmFjay5sZW5ndGgsXG4gICAgICAgIG5vcm1hbGl6ZWRMZW5ndGg6IG5vcm1hbGl6ZWRGZWVkYmFjay5sZW5ndGgsXG4gICAgICAgIGhhc1VuaWNvZGVJc3N1ZXM6ICF2YWxpZGF0aW9uUmVzdWx0LmlzVmFsaWQsXG4gICAgICAgIGRldGVjdGVkSXNzdWVzOiB2YWxpZGF0aW9uUmVzdWx0LmRldGVjdGVkSXNzdWVzXG4gICAgICB9XG4gICAgfSk7XG4gICAgXG4gICAgLy8gVmFsaWRhdGUgaW5wdXQgbGVuZ3RoIHRvIHByZXZlbnQgUmVEb1NcbiAgICBpZiAobm9ybWFsaXplZEZlZWRiYWNrLmxlbmd0aCA+IHRoaXMuTUFYX0ZFRURCQUNLX0xFTkdUSCkge1xuICAgICAgbG9nZ2VyLndhcm4oYEZlZWRiYWNrIHRydW5jYXRlZCBmcm9tICR7bm9ybWFsaXplZEZlZWRiYWNrLmxlbmd0aH0gdG8gJHt0aGlzLk1BWF9GRUVEQkFDS19MRU5HVEh9IGNoYXJhY3RlcnNgKTtcbiAgICAgIG5vcm1hbGl6ZWRGZWVkYmFjayA9IG5vcm1hbGl6ZWRGZWVkYmFjay5zdWJzdHJpbmcoMCwgdGhpcy5NQVhfRkVFREJBQ0tfTEVOR1RIKTtcbiAgICB9XG5cbiAgICAvLyBBbmFseXplIHNlbnRpbWVudFxuICAgIGNvbnN0IHNlbnRpbWVudCA9IGF3YWl0IHRoaXMuYW5hbHl6ZVNlbnRpbWVudChub3JtYWxpemVkRmVlZGJhY2spO1xuICAgIFxuICAgIC8vIEluZmVyIHJhdGluZ1xuICAgIGNvbnN0IGluZmVycmVkUmF0aW5nID0gYXdhaXQgdGhpcy5pbmZlclJhdGluZyhub3JtYWxpemVkRmVlZGJhY2spO1xuICAgIFxuICAgIC8vIEV4dHJhY3Qga2V5d29yZHNcbiAgICBjb25zdCBrZXl3b3JkcyA9IHRoaXMuZXh0cmFjdEtleXdvcmRzKG5vcm1hbGl6ZWRGZWVkYmFjayk7XG4gICAgXG4gICAgLy8gRXh0cmFjdCBzdWdnZXN0aW9uc1xuICAgIGNvbnN0IHN1Z2dlc3Rpb25zID0gYXdhaXQgdGhpcy5leHRyYWN0U3VnZ2VzdGlvbnMobm9ybWFsaXplZEZlZWRiYWNrKTtcbiAgICBcbiAgICAvLyBFeHRyYWN0IGVudGl0aWVzXG4gICAgY29uc3QgZW50aXRpZXMgPSB0aGlzLmV4dHJhY3RFbnRpdGllcyhub3JtYWxpemVkRmVlZGJhY2spO1xuICAgIFxuICAgIC8vIENhbGN1bGF0ZSBjb25maWRlbmNlIGJhc2VkIG9uIGNsYXJpdHkgb2YgZmVlZGJhY2tcbiAgICBjb25zdCBjb25maWRlbmNlID0gdGhpcy5jYWxjdWxhdGVDb25maWRlbmNlKG5vcm1hbGl6ZWRGZWVkYmFjaywgc2VudGltZW50LCBpbmZlcnJlZFJhdGluZyk7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIG9yaWdpbmFsRmVlZGJhY2s6IG5vcm1hbGl6ZWRGZWVkYmFjayxcbiAgICAgIHNlbnRpbWVudCxcbiAgICAgIGluZmVycmVkUmF0aW5nOiBpbmZlcnJlZFJhdGluZyA/PyB1bmRlZmluZWQsXG4gICAgICBjb25maWRlbmNlLFxuICAgICAga2V5d29yZHMsXG4gICAgICBzdWdnZXN0aW9ucyxcbiAgICAgIGVudGl0aWVzXG4gICAgfTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEFuYWx5emUgc2VudGltZW50IGZyb20gdGV4dC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBhbmFseXplU2VudGltZW50KHRleHQ6IHN0cmluZyk6IFByb21pc2U8J3Bvc2l0aXZlJyB8ICduZWdhdGl2ZScgfCAnbmV1dHJhbCc+IHtcbiAgICBjb25zdCBub3JtYWxpemVkID0gdGV4dC50b0xvd2VyQ2FzZSgpO1xuICAgIGNvbnN0IHNjb3JlcyA9IHtcbiAgICAgIHBvc2l0aXZlOiAwLFxuICAgICAgbmVnYXRpdmU6IDAsXG4gICAgICBuZXV0cmFsOiAwXG4gICAgfTtcbiAgICBcbiAgICAvLyBDaGVjayBlYWNoIHNlbnRpbWVudCBjYXRlZ29yeVxuICAgIGZvciAoY29uc3QgWywgY29uZmlnXSBvZiBPYmplY3QuZW50cmllcyh0aGlzLnNlbnRpbWVudFBhdHRlcm5zKSkge1xuICAgICAgZm9yIChjb25zdCBwYXR0ZXJuIG9mIGNvbmZpZy5wYXR0ZXJucykge1xuICAgICAgICBpZiAobm9ybWFsaXplZC5pbmNsdWRlcyhwYXR0ZXJuKSkge1xuICAgICAgICAgIHNjb3Jlc1tjb25maWcuc2VudGltZW50XSArPSB0aGlzLmdldFBhdHRlcm5XZWlnaHQocGF0dGVybiwgbm9ybWFsaXplZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLy8gQWRqdXN0IGZvciBuZWdhdGlvbnMgLSBtb3JlIHNvcGhpc3RpY2F0ZWQgaGFuZGxpbmdcbiAgICBjb25zdCBuZWdhdGlvblBhdHRlcm5zID0gWydub3QnLCAnbm8nLCAnbmV2ZXInLCAnbmVpdGhlcicsICdub3InLCAnblxcJ3QnXTtcbiAgICBmb3IgKGNvbnN0IG5lZ2F0aW9uIG9mIG5lZ2F0aW9uUGF0dGVybnMpIHtcbiAgICAgIC8vIENoZWNrIGZvciBjb21tb24gcG9zaXRpdmUgbmVnYXRpb24gcGF0dGVybnNcbiAgICAgIGlmIChub3JtYWxpemVkLmluY2x1ZGVzKGAke25lZ2F0aW9ufSBiYWRgKSB8fCBcbiAgICAgICAgICBub3JtYWxpemVkLmluY2x1ZGVzKGAke25lZ2F0aW9ufSB0ZXJyaWJsZWApIHx8XG4gICAgICAgICAgbm9ybWFsaXplZC5pbmNsdWRlcyhgJHtuZWdhdGlvbn0gcG9vcmApKSB7XG4gICAgICAgIHNjb3Jlcy5wb3NpdGl2ZSArPSAxO1xuICAgICAgICBzY29yZXMubmVnYXRpdmUgPSBNYXRoLm1heCgwLCBzY29yZXMubmVnYXRpdmUgLSAxKTtcbiAgICAgIH1cbiAgICAgIC8vIENoZWNrIGZvciBuZWdhdGl2ZSBuZWdhdGlvbiBwYXR0ZXJuc1xuICAgICAgZWxzZSBpZiAobm9ybWFsaXplZC5pbmNsdWRlcyhgJHtuZWdhdGlvbn0gZ29vZGApIHx8XG4gICAgICAgICAgICAgICBub3JtYWxpemVkLmluY2x1ZGVzKGAke25lZ2F0aW9ufSBncmVhdGApKSB7XG4gICAgICAgIHNjb3Jlcy5uZWdhdGl2ZSArPSAxO1xuICAgICAgICBzY29yZXMucG9zaXRpdmUgPSBNYXRoLm1heCgwLCBzY29yZXMucG9zaXRpdmUgLSAxKTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLy8gRGV0ZXJtaW5lIGRvbWluYW50IHNlbnRpbWVudFxuICAgIGlmIChzY29yZXMucG9zaXRpdmUgPiBzY29yZXMubmVnYXRpdmUgJiYgc2NvcmVzLnBvc2l0aXZlID4gc2NvcmVzLm5ldXRyYWwpIHtcbiAgICAgIHJldHVybiAncG9zaXRpdmUnO1xuICAgIH0gZWxzZSBpZiAoc2NvcmVzLm5lZ2F0aXZlID4gc2NvcmVzLnBvc2l0aXZlICYmIHNjb3Jlcy5uZWdhdGl2ZSA+IHNjb3Jlcy5uZXV0cmFsKSB7XG4gICAgICByZXR1cm4gJ25lZ2F0aXZlJztcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuICduZXV0cmFsJztcbiAgfVxuICBcbiAgLyoqXG4gICAqIEluZmVyIG51bWVyaWMgcmF0aW5nIGZyb20gdGV4dC5cbiAgICogRklYOiBVc2UgU2FmZVJlZ2V4IGZvciBET1MgcHJvdGVjdGlvbiAoUFIgIzExODcpXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgaW5mZXJSYXRpbmcodGV4dDogc3RyaW5nKTogUHJvbWlzZTxudW1iZXIgfCBudWxsPiB7XG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IHRleHQudG9Mb3dlckNhc2UoKTtcblxuICAgIC8vIEZJWDogRE9TIHByb3RlY3Rpb24gLSBwYXR0ZXJucyBhcmUgc3RhdGljIGJ1dCBvcGVyYXRlIG9uIHVzZXIgaW5wdXRcbiAgICAvLyBDaGVjayBmb3IgZXhwbGljaXQgcmF0aW5nc1xuICAgIGNvbnN0IGV4cGxpY2l0UGF0dGVybnMgPSBbXG4gICAgICAvKFxcZCspXFxzKihzdGFycz98XFwvXFxzKjV8b3V0XFxzKm9mXFxzKjUpLyxcbiAgICAgIC9yYXRlXFxzKig/Oml0XFxzKik/KFxcZCspLyxcbiAgICAgIC9yYXRpbmdbOlxcc10rKFxcZCspLyxcbiAgICAgIC9zY29yZVs6XFxzXSsoXFxkKykvXG4gICAgXTtcblxuICAgIGZvciAoY29uc3QgcGF0dGVybiBvZiBleHBsaWNpdFBhdHRlcm5zKSB7XG4gICAgICAvLyBGSVg6IFVzZSBTYWZlUmVnZXgubWF0Y2ggaW5zdGVhZCBvZiBTdHJpbmcubWF0Y2hcbiAgICAgIC8vIFByZXZpb3VzbHk6IG5vcm1hbGl6ZWQubWF0Y2gocGF0dGVybikgLSBubyB0aW1lb3V0IHByb3RlY3Rpb25cbiAgICAgIC8vIE5vdzogU2FmZVJlZ2V4Lm1hdGNoIHdpdGggdGltZW91dCBhbmQgbGVuZ3RoIHZhbGlkYXRpb25cbiAgICAgIGNvbnN0IG1hdGNoID0gU2FmZVJlZ2V4Lm1hdGNoKG5vcm1hbGl6ZWQsIHBhdHRlcm4sIHtcbiAgICAgICAgY29udGV4dDogJ0ZlZWRiYWNrUHJvY2Vzc29yLmluZmVyUmF0aW5nJyxcbiAgICAgICAgdGltZW91dDogMTAwXG4gICAgICB9KTtcbiAgICAgIGlmIChtYXRjaCkge1xuICAgICAgICBjb25zdCByYXRpbmcgPSBOdW1iZXIucGFyc2VJbnQobWF0Y2hbMV0pO1xuICAgICAgICBpZiAocmF0aW5nID49IDEgJiYgcmF0aW5nIDw9IDUpIHtcbiAgICAgICAgICByZXR1cm4gcmF0aW5nO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRklYOiBET1MgcHJvdGVjdGlvbiBmb3IgcGVyY2VudCBwYXR0ZXJuXG4gICAgLy8gQ2hlY2sgZm9yIHBlcmNlbnRhZ2UgcmF0aW5nc1xuICAgIGNvbnN0IHBlcmNlbnRNYXRjaCA9IFNhZmVSZWdleC5tYXRjaChub3JtYWxpemVkLCAvKFxcZCspXFxzKiUvLCB7XG4gICAgICBjb250ZXh0OiAnRmVlZGJhY2tQcm9jZXNzb3IuaW5mZXJSYXRpbmctcGVyY2VudCcsXG4gICAgICB0aW1lb3V0OiAxMDBcbiAgICB9KTtcbiAgICBpZiAocGVyY2VudE1hdGNoKSB7XG4gICAgICBjb25zdCBwZXJjZW50ID0gTnVtYmVyLnBhcnNlSW50KHBlcmNlbnRNYXRjaFsxXSk7XG4gICAgICBpZiAocGVyY2VudCA+PSAwICYmIHBlcmNlbnQgPD0gMTAwKSB7XG4gICAgICAgIHJldHVybiBNYXR