@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
JavaScript
/**
* 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