@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.
462 lines • 65.3 kB
JavaScript
/**
* FIX: DMCP-SEC-006 - Security audit suppression
* This file contains only helper utilities for element operations.
* No audit logging is required - logging happens in the calling handlers.
* @security-audit-suppress DMCP-SEC-006
*/
import { slugify } from '../../utils/filesystem.js';
import { ElementType } from '../../portfolio/PortfolioManager.js';
import { logger } from '../../utils/logger.js';
import { SecurityMonitor } from '../../security/securityMonitor.js';
import { ELEMENT_TYPE_MAP, ALL_ELEMENT_TYPES, formatElementTypesList as sharedFormatElementTypesList, } from '../../utils/elementTypeNormalization.js';
import { parseElementPolicy, analyzePatternSyntax, getGatekeeperAuthoringErrors } from '../mcp-aql/policies/ElementPolicies.js';
import { findPatternConflicts } from '../../utils/patternMatcher.js';
export function findElementFlexibly(name, elementList) {
if (!name || !Array.isArray(elementList) || elementList.length === 0) {
return undefined;
}
const searchNameLower = name.toLowerCase();
const searchNameSlug = slugify(name);
let element = elementList.find((e) => e.metadata?.name?.toLowerCase() === searchNameLower);
if (!element) {
element = elementList.find((e) => {
const elementSlug = slugify(e.metadata?.name || '');
return elementSlug === searchNameSlug || elementSlug === searchNameLower;
});
}
if (!element) {
element = elementList.find((e) => {
const elementName = e.metadata?.name || '';
const elementSlug = slugify(elementName);
return (elementSlug.includes(searchNameSlug) ||
elementName.toLowerCase().includes(searchNameLower));
});
}
return element;
}
export function sanitizeMetadata(metadata) {
if (!metadata || typeof metadata !== 'object') {
return {};
}
// FIX: DMCP-SEC-006 - Add security audit logging for sanitization
SecurityMonitor.logSecurityEvent({
type: 'ELEMENT_VALIDATED',
severity: 'LOW',
source: 'helpers.sanitizeMetadata',
details: 'Metadata sanitized for element creation/update',
additionalData: { fieldCount: Object.keys(metadata).length }
});
const dangerousProperties = ['__proto__', 'constructor', 'prototype'];
const sanitized = {};
for (const [key, value] of Object.entries(metadata)) {
if (dangerousProperties.includes(key)) {
continue;
}
if (value && typeof value === 'object' && !Array.isArray(value)) {
sanitized[key] = sanitizeMetadata(value);
}
else {
sanitized[key] = value;
}
}
return sanitized;
}
function asMetadataRecord(value) {
return value && typeof value === 'object' && !Array.isArray(value)
? value
: undefined;
}
export function collectGatekeeperAuthoringErrors(input, metadata) {
const normalizeGatekeeperError = (error) => error.replace(/^Invalid gatekeeper policy:\s*/, '').trim();
return [...new Set([
...getGatekeeperAuthoringErrors(input),
...getGatekeeperAuthoringErrors(asMetadataRecord(metadata)),
].map(normalizeGatekeeperError))];
}
export function formatGatekeeperValidationMessage(errors) {
const uniqueErrors = [...new Set(errors)];
return [
'Gatekeeper policy validation failed:',
...uniqueErrors.map(error => ` • ${error}`),
].join('\n');
}
// Delegate to shared normalization utility (Issue #433)
const ELEMENT_TYPE_ALIASES = ELEMENT_TYPE_MAP;
const ELEMENT_TYPE_LABELS = {
[ElementType.PERSONA]: { singular: 'persona', plural: 'personas' },
[ElementType.SKILL]: { singular: 'skill', plural: 'skills' },
[ElementType.TEMPLATE]: { singular: 'template', plural: 'templates' },
[ElementType.AGENT]: { singular: 'agent', plural: 'agents' },
[ElementType.MEMORY]: { singular: 'memory', plural: 'memories' },
[ElementType.ENSEMBLE]: { singular: 'ensemble', plural: 'ensembles' }
};
export function normalizeElementTypeInput(rawType) {
if (!rawType || typeof rawType !== 'string') {
return { type: null, aliasUsed: false };
}
const normalizedKey = rawType.trim().toLowerCase();
const resolvedType = ELEMENT_TYPE_ALIASES[normalizedKey];
if (!resolvedType) {
return { type: null, aliasUsed: false };
}
const aliasUsed = !ALL_ELEMENT_TYPES.includes(rawType) && normalizedKey !== resolvedType;
if (aliasUsed) {
logger.warn(`Using singular element type '${rawType}' is deprecated. Please use '${resolvedType}' instead.`);
}
return { type: resolvedType, aliasUsed };
}
export function formatValidElementTypesList() {
return sharedFormatElementTypesList();
}
export function getElementTypeLabel(type, options = {}) {
const label = ELEMENT_TYPE_LABELS[type];
if (!label) {
return options.plural ? type : type.replace(/s$/, '');
}
return options.plural ? label.plural : label.singular;
}
export function getElementFilename(type, name) {
const safeSlug = slugify(name || '') || 'untitled';
const extension = type === ElementType.MEMORY ? '.yaml' : '.md';
return `${safeSlug}${extension}`;
}
/**
* Known metadata properties for each element type.
* Used by both `detectUnknownMetadataProperties()` (create/edit warnings)
* and `editElement()` (field routing to metadata vs element object).
*
* IMPORTANT: Both camelCase and snake_case variants must be listed where
* applicable, since LLMs may send either form. The lookup uses exact
* string matching via `Set.has()`.
*
* @example
* // Common properties shared across all types:
* 'name', 'description', 'author', 'version', 'tags', 'gatekeeper'
*
* @example
* // Type-specific properties (only valid for that element type):
* Agent: 'goal', 'activates', 'tools', 'autonomy', 'resilience'
* Skill: 'domains', 'category', 'prerequisites'
* Ensemble: 'elements', 'activationStrategy', 'activation_strategy'
*/
export const KNOWN_METADATA_PROPERTIES = {
[ElementType.PERSONA]: new Set([
// Common metadata
'name', 'description', 'author', 'version', 'tags', 'created', 'modified',
// Gatekeeper policy (all element types)
'gatekeeper',
// Persona-specific
'triggers', 'tone', 'voice', 'domain', 'context', 'style', 'instructions',
'personality', 'expertise', 'communication_style', 'communicationStyle'
]),
[ElementType.SKILL]: new Set([
// Common metadata
'name', 'description', 'author', 'version', 'tags', 'created', 'modified',
// Gatekeeper policy (all element types)
'gatekeeper',
// Skill-specific
'triggers', 'domain', 'domains', 'category', 'examples', 'prerequisites',
'content', 'instructions', 'usage', 'capabilities'
]),
[ElementType.TEMPLATE]: new Set([
// Common metadata
'name', 'description', 'author', 'version', 'tags', 'created', 'modified',
// Gatekeeper policy (all element types)
'gatekeeper',
// Template-specific
'variables', 'template', 'category', 'outputFormat', 'output_format',
'content', 'instructions', 'format', 'schema'
]),
[ElementType.AGENT]: new Set([
// Common metadata
'name', 'description', 'author', 'version', 'tags', 'created', 'modified',
// Gatekeeper policy (all element types)
'gatekeeper',
// Agent V1 fields (legacy, still supported)
'goals', 'constraints', 'capabilities', 'triggers', 'state', 'actions',
'decisionFramework', 'decision_framework', 'skills',
// Agent V2 fields (v2.0.0 Agentic Loop Redesign)
'goal', 'activates', 'tools', 'systemPrompt', 'system_prompt',
'autonomy', 'resilience', 'successCriteria', 'success_criteria',
// Issue #585: Body content fields (stored in extensions.instructions)
'content', 'instructions',
// V1 backward compatibility
'specializations', 'riskTolerance', 'risk_tolerance',
'learningEnabled', 'learning_enabled', 'maxConcurrentGoals', 'max_concurrent_goals'
]),
[ElementType.MEMORY]: new Set([
// Common metadata
'name', 'description', 'author', 'version', 'tags', 'created', 'modified',
// Gatekeeper policy (all element types)
'gatekeeper',
// Memory-specific
'id', 'entries', 'instructions', 'retentionPolicy', 'retention_policy', 'maxEntries',
'max_entries', 'category', 'scope'
]),
[ElementType.ENSEMBLE]: new Set([
// Common metadata
'name', 'description', 'author', 'version', 'tags', 'created', 'modified',
// Gatekeeper policy (all element types)
'gatekeeper',
// Ensemble-specific - NOTE: 'elements' is correct, NOT 'members'
'elements', 'instructions', 'activationStrategy', 'activation_strategy',
'conflictResolution', 'conflict_resolution',
'contextSharing', 'context_sharing',
'resourceLimits', 'resource_limits',
'allowNested', 'allow_nested',
'maxNestingDepth', 'max_nesting_depth'
])
};
/**
* Common typos and their corrections for better LLM feedback.
*
* IMPORTANT: All keys MUST be lowercase. The lookup uses `key.toLowerCase()`
* to ensure case-insensitive matching.
*
* @example
* // Global correction (applies to all element types):
* 'discription': { correct: 'description' }
*
* @example
* // Type-specific correction (only applies to specified types):
* 'members': { correct: 'elements', elementTypes: [ElementType.ENSEMBLE] }
*/
const PROPERTY_CORRECTIONS = {
// Ensemble: 'members' is a common mistake - should be 'elements'
'members': { correct: 'elements', elementTypes: [ElementType.ENSEMBLE] },
'member': { correct: 'elements', elementTypes: [ElementType.ENSEMBLE] },
// Common typos
'discription': { correct: 'description' },
'desciption': { correct: 'description' },
'auther': { correct: 'author' },
'tages': { correct: 'tags' },
'varibles': { correct: 'variables', elementTypes: [ElementType.TEMPLATE] },
'activiation': { correct: 'activation' },
'stratergy': { correct: 'strategy' },
'elemets': { correct: 'elements', elementTypes: [ElementType.ENSEMBLE] },
// Snake_case vs camelCase mismatches
'activationstrategy': { correct: 'activationStrategy', elementTypes: [ElementType.ENSEMBLE] },
'conflictresolution': { correct: 'conflictResolution', elementTypes: [ElementType.ENSEMBLE] },
'contextsharing': { correct: 'contextSharing', elementTypes: [ElementType.ENSEMBLE] }
};
/**
* Validate a gatekeeper policy in element metadata.
* Returns warnings (not throws) for invalid policy structures,
* so LLMs get immediate feedback about malformed policies.
*
* @param metadata - The metadata object containing the gatekeeper policy
* @returns Array of warnings for invalid policy structure (empty if valid)
*/
export function validateGatekeeperPolicy(metadata) {
if (!metadata.gatekeeper) {
return [];
}
try {
const policy = parseElementPolicy(metadata);
const warnings = [];
// Issue #674: Warn when an operation appears in both allow and confirm lists
// The most restrictive policy (confirm) will apply, but the overlap is likely a mistake
if (policy?.allow?.length && policy?.confirm?.length) {
const allowSet = new Set(policy.allow);
for (const op of policy.confirm) {
if (allowSet.has(op)) {
warnings.push({
property: 'gatekeeper',
message: `Operation '${op}' appears in both allow and confirm — most restrictive policy (confirm) will apply.`,
});
}
}
}
// Issue #625 Phase 2: Detect allow/deny pattern conflicts
if (policy?.externalRestrictions) {
const { allowPatterns, denyPatterns, confirmPatterns } = policy.externalRestrictions;
if (allowPatterns?.length && denyPatterns?.length) {
const conflicts = findPatternConflicts(denyPatterns, allowPatterns);
for (const conflict of conflicts) {
warnings.push({
property: 'gatekeeper.externalRestrictions',
message: `Pattern conflict (deny takes precedence): ${conflict}`,
});
}
}
// Issue #1660: Detect confirm/deny pattern conflicts
if (confirmPatterns?.length && denyPatterns?.length) {
const conflicts = findPatternConflicts(denyPatterns, confirmPatterns);
for (const conflict of conflicts) {
warnings.push({
property: 'gatekeeper.externalRestrictions',
message: `Pattern conflict (deny takes precedence over confirm): ${conflict}`,
});
}
}
// Issue #1664: Enhanced pattern syntax validation
const allPatternArrays = [
[denyPatterns, 'denyPatterns'],
[confirmPatterns, 'confirmPatterns'],
[allowPatterns, 'allowPatterns'],
];
for (const [patterns, fieldName] of allPatternArrays) {
if (patterns?.length) {
const syntaxWarnings = analyzePatternSyntax(patterns, fieldName);
for (const warning of syntaxWarnings) {
warnings.push({
property: 'gatekeeper.externalRestrictions',
message: warning,
});
}
}
}
}
return warnings;
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
return [{
property: 'gatekeeper',
message: message,
}];
}
}
/**
* Check metadata for unknown properties and return warnings.
* This helps LLMs correct their behavior in real-time when they use wrong property names.
*
* @param elementType - The type of element being created/edited
* @param metadata - The metadata object to check
* @returns Array of warnings for unknown properties
*/
// Fields handled by dedicated branches in editElement, not metadata routing.
// These should not trigger "unknown property" warnings since they ARE processed.
const SPECIAL_ROUTE_FIELDS = new Set([
'instructions', 'content', 'elements', 'metadata', 'entries', 'version'
]);
export function detectUnknownMetadataProperties(elementType, metadata) {
if (!metadata || typeof metadata !== 'object') {
return [];
}
const knownProperties = KNOWN_METADATA_PROPERTIES[elementType];
if (!knownProperties) {
return [];
}
const warnings = [];
for (const key of Object.keys(metadata)) {
// Skip if it's a known property
if (knownProperties.has(key)) {
continue;
}
// Skip special route fields — these are handled by dedicated code paths
// in editElement (not metadata routing), so warning is misleading
if (SPECIAL_ROUTE_FIELDS.has(key)) {
continue;
}
// Check if it's a known typo with a correction
const correction = PROPERTY_CORRECTIONS[key.toLowerCase()];
if (correction) {
// Check if correction applies to this element type
const appliesToType = !correction.elementTypes ||
correction.elementTypes.includes(elementType);
if (appliesToType) {
warnings.push({
property: key,
suggestion: correction.correct,
message: `Unknown property '${key}' - did you mean '${correction.correct}'?`
});
continue;
}
}
// Generic unknown property warning
warnings.push({
property: key,
message: `Unknown property '${key}' for ${getElementTypeLabel(elementType)} - this property will be ignored`
});
}
// Validate gatekeeper policy structure if present
if (metadata.gatekeeper) {
warnings.push(...validateGatekeeperPolicy(metadata));
}
return warnings;
}
/**
* Format unknown property warnings for MCP response.
* Returns a warning block that can be prepended to success messages.
*/
export function formatUnknownPropertyWarnings(warnings) {
if (warnings.length === 0) {
return '';
}
const lines = ['⚠️ **Metadata Warnings:**'];
for (const warning of warnings) {
if (warning.suggestion) {
lines.push(` • ${warning.message} Use '${warning.suggestion}' instead.`);
}
else {
lines.push(` • ${warning.message}`);
}
}
lines.push(''); // Empty line after warnings
return lines.join('\n');
}
/**
* Format element resolution warnings for MCP response.
* Surfaces disambiguation and not-found info from resolveElementTypes()
* so the user can fix missing element_type fields.
*
* Follows the same pattern as formatUnknownPropertyWarnings().
*/
export function formatElementResolutionWarnings(result) {
if (result.ambiguous.length === 0 && result.notFound.length === 0) {
return '';
}
const lines = ['⚠️ **Element Resolution Warnings:**'];
for (const item of result.ambiguous) {
lines.push(` • '${item.element_name}' exists as multiple types (${item.found_in.join(', ')}) — specify element_type to disambiguate`);
}
for (const name of result.notFound) {
lines.push(` • '${name}' not found in portfolio — provide element_type explicitly or ensure the element exists`);
}
lines.push(''); // Empty line after warnings
return lines.join('\n');
}
export async function resolveElementByName(manager, type, name) {
if (!manager) {
return undefined;
}
if (typeof manager.list === 'function') {
const elements = await manager.list();
if (Array.isArray(elements) && elements.length > 0) {
const found = findElementFlexibly(name, elements);
if (found) {
return found;
}
}
}
if (typeof manager.find === 'function') {
const lowered = name.toLowerCase();
const slug = slugify(name);
const found = await manager.find((candidate) => {
const candidateName = candidate?.metadata?.name;
if (typeof candidateName !== 'string') {
return false;
}
const candidateSlug = slugify(candidateName);
return candidateName.toLowerCase() === lowered || candidateSlug === slug;
});
if (found) {
return found;
}
}
if (typeof manager.load === 'function') {
const candidateFilename = getElementFilename(type, name);
try {
return await manager.load(candidateFilename);
}
catch (error) {
logger.debug(`[ElementCRUD] Direct load failed for ${type}:${name}`, {
error: error instanceof Error ? error.message : String(error)
});
}
}
logger.debug(`[ElementCRUD] Unable to locate ${type}:${name} via available manager operations.`);
return undefined;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9oYW5kbGVycy9lbGVtZW50LWNydWQvaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7R0FLRztBQUVILE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDbEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLGlCQUFpQixFQUNqQixzQkFBc0IsSUFBSSw0QkFBNEIsR0FDdkQsTUFBTSx5Q0FBeUMsQ0FBQztBQUNqRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsb0JBQW9CLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUNoSSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUVyRSxNQUFNLFVBQVUsbUJBQW1CLENBQ2pDLElBQVksRUFDWixXQUFnQjtJQUVoQixJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxXQUFXLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3JFLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDM0MsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRXJDLElBQUksT0FBTyxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQzVCLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsS0FBSyxlQUFl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LE9BQU8sT0FBTyxDQUFDLElBQUksS0FBSyxVQUFVLEVBQUUsQ0FBQztRQUN2QyxNQUFNLGlCQUFpQixHQUFHLGtCQUFrQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN6RCxJQUFJLENBQUM7WUFDSCxPQUFPLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQyx3Q0FBd0MsSUFBSSxJQUFJLElBQUksRUFBRSxFQUFFO2dCQUNuRSxLQUFLLEVBQUUsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQzthQUM5RCxDQUFDLENBQUM7UUFDTCxDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sQ0FBQyxLQUFLLENBQUMsa0NBQWtDLElBQUksSUFBSSxJQUFJLG9DQUFvQyxDQUFDLENBQUM7SUFDakcsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRklYOiBETUNQLVNFQy0wMDYgLSBTZWN1cml0eSBhdWRpdCBzdXBwcmVzc2lvblxuICogVGhpcyBmaWxlIGNvbnRhaW5zIG9ubHkgaGVscGVyIHV0aWxpdGllcyBmb3IgZWxlbWVudCBvcGVyYXRpb25zLlxuICogTm8gYXVkaXQgbG9nZ2luZyBpcyByZXF1aXJlZCAtIGxvZ2dpbmcgaGFwcGVucyBpbiB0aGUgY2FsbGluZyBoYW5kbGVycy5cbiAqIEBzZWN1cml0eS1hdWRpdC1zdXBwcmVzcyBETUNQLVNFQy0wMDZcbiAqL1xuXG5pbXBvcnQgeyBzbHVnaWZ5IH0gZnJvbSAnLi4vLi4vdXRpbHMvZmlsZXN5c3RlbS5qcyc7XG5pbXBvcnQgeyBFbGVtZW50VHlwZSB9IGZyb20gJy4uLy4uL3BvcnRmb2xpby9Qb3J0Zm9saW9NYW5hZ2VyLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHtcbiAgRUxFTUVOVF9UWVBFX01BUCxcbiAgQUxMX0VMRU1FTlRfVFlQRVMsXG4gIGZvcm1hdEVsZW1lbnRUeXBlc0xpc3QgYXMgc2hhcmVkRm9ybWF0RWxlbWVudFR5cGVzTGlzdCxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvZWxlbWVudFR5cGVOb3JtYWxpemF0aW9uLmpzJztcbmltcG9ydCB7IHBhcnNlRWxlbWVudFBvbGljeSwgYW5hbHl6ZVBhdHRlcm5TeW50YXgsIGdldEdhdGVrZWVwZXJBdXRob3JpbmdFcnJvcnMgfSBmcm9tICcuLi9tY3AtYXFsL3BvbGljaWVzL0VsZW1lbnRQb2xpY2llcy5qcyc7XG5pbXBvcnQgeyBmaW5kUGF0dGVybkNvbmZsaWN0cyB9IGZyb20gJy4uLy4uL3V0aWxzL3BhdHRlcm5NYXRjaGVyLmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGZpbmRFbGVtZW50RmxleGlibHk8VCBleHRlbmRzIHsgbWV0YWRhdGE/OiB7IG5hbWU/OiBzdHJpbmcgfSB9PihcbiAgbmFtZTogc3RyaW5nLFxuICBlbGVtZW50TGlzdDogVFtdXG4pOiBUIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCFuYW1lIHx8ICFBcnJheS5pc0FycmF5KGVsZW1lbnRMaXN0KSB8fCBlbGVtZW50TGlzdC5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgY29uc3Qgc2VhcmNoTmFtZUxvd2VyID0gbmFtZS50b0xvd2VyQ2FzZSgpO1xuICBjb25zdCBzZWFyY2hOYW1lU2x1ZyA9IHNsdWdpZnkobmFtZSk7XG5cbiAgbGV0IGVsZW1lbnQgPSBlbGVtZW50TGlzdC5maW5kKFxuICAgIChlKSA9PiBlLm1ldGFkYXRhPy5uYW1lPy50b0xvd2VyQ2FzZSgpID09PSBzZWFyY2hOYW1lTG93ZXJcbiAgKTtcblxuICBpZiAoIWVsZW1lbnQpIHtcbiAgICBlbGVtZW50ID0gZWxlbWVudExpc3QuZmluZCgoZSkgPT4ge1xuICAgICAgY29uc3QgZWxlbWVudFNsdWcgPSBzbHVnaWZ5KGUubWV0YWRhdGE/Lm5hbWUgfHwgJycpO1xuICAgICAgcmV0dXJuIGVsZW1lbnRTbHVnID09PSBzZWFyY2hOYW1lU2x1ZyB8fCBlbGVtZW50U2x1ZyA9PT0gc2VhcmNoTmFtZUxvd2VyO1xuICAgIH0pO1xuICB9XG5cbiAgaWYgKCFlbGVtZW50KSB7XG4gICAgZWxlbWVudCA9IGVsZW1lbnRMaXN0LmZpbmQoKGUpID0+IHtcbiAgICAgIGNvbnN0IGVsZW1lbnROYW1lID0gZS5tZXRhZGF0YT8ubmFtZSB8fCAnJztcbiAgICAgIGNvbnN0IGVsZW1lbnRTbHVnID0gc2x1Z2lmeShlbGVtZW50TmFtZSk7XG4gICAgICByZXR1cm4gKFxuICAgICAgICBlbGVtZW50U2x1Zy5pbmNsdWRlcyhzZWFyY2hOYW1lU2x1ZykgfHxcbiAgICAgICAgZWxlbWVudE5hbWUudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhzZWFyY2hOYW1lTG93ZXIpXG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIGVsZW1lbnQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzYW5pdGl6ZU1ldGFkYXRhKG1ldGFkYXRhOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHwgdW5kZWZpbmVkKTogUmVjb3JkPHN0cmluZywgYW55PiB7XG4gIGlmICghbWV0YWRhdGEgfHwgdHlwZW9mIG1ldGFkYXRhICE9PSAnb2JqZWN0Jykge1xuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIC8vIEZJWDogRE1DUC1TRUMtMDA2IC0gQWRkIHNlY3VyaXR5IGF1ZGl0IGxvZ2dpbmcgZm9yIHNhbml0aXphdGlvblxuICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgdHlwZTogJ0VMRU1FTlRfVkFMSURBVEVEJyxcbiAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgc291cmNlOiAnaGVscGVycy5zYW5pdGl6ZU1ldGFkYXRhJyxcbiAgICBkZXRhaWxzOiAnTWV0YWRhdGEgc2FuaXRpemVkIGZvciBlbGVtZW50IGNyZWF0aW9uL3VwZGF0ZScsXG4gICAgYWRkaXRpb25hbERhdGE6IHsgZmllbGRDb3VudDogT2JqZWN0LmtleXMobWV0YWRhdGEpLmxlbmd0aCB9XG4gIH0pO1xuXG4gIGNvbnN0IGRhbmdlcm91c1Byb3BlcnRpZXMgPSBbJ19fcHJvdG9fXycsICdjb25zdHJ1Y3RvcicsICdwcm90b3R5cGUnXTtcbiAgY29uc3Qgc2FuaXRpemVkOiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID0ge307XG5cbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobWV0YWRhdGEpKSB7XG4gICAgaWYgKGRhbmdlcm91c1Byb3BlcnRpZXMuaW5jbHVkZXMoa2V5KSkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgaWYgKHZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICBzYW5pdGl6ZWRba2V5XSA9IHNhbml0aXplTWV0YWRhdGEodmFsdWUgYXMgUmVjb3JkPHN0cmluZywgYW55Pik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNhbml0aXplZFtrZXldID0gdmFsdWU7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHNhbml0aXplZDtcbn1cblxuZnVuY3Rpb24gYXNNZXRhZGF0YVJlY29yZCh2YWx1ZTogdW5rbm93bik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkodmFsdWUpXG4gICAgPyB2YWx1ZSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPlxuICAgIDogdW5kZWZpbmVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29sbGVjdEdhdGVrZWVwZXJBdXRob3JpbmdFcnJvcnMoXG4gIGlucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCxcbiAgbWV0YWRhdGE/OiB1bmtub3duXG4pOiBzdHJpbmdbXSB7XG4gIGNvbnN0IG5vcm1hbGl6ZUdhdGVrZWVwZXJFcnJvciA9IChlcnJvcjogc3RyaW5nKTogc3RyaW5nID0+XG4gICAgZXJyb3IucmVwbGFjZSgvXkludmFsaWQgZ2F0ZWtlZXBlciBwb2xpY3k6XFxzKi8sICcnKS50cmltKCk7XG5cbiAgcmV0dXJuIFsuLi5uZXcgU2V0KFtcbiAgICAuLi5nZXRHYXRla2VlcGVyQXV0aG9yaW5nRXJyb3JzKGlucHV0KSxcbiAgICAuLi5nZXRHYXRla2VlcGVyQXV0aG9yaW5nRXJyb3JzKGFzTWV0YWRhdGFSZWNvcmQobWV0YWRhdGEpKSxcbiAgXS5tYXAobm9ybWFsaXplR2F0ZWtlZXBlckVycm9yKSldO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0R2F0ZWtlZXBlclZhbGlkYXRpb25NZXNzYWdlKGVycm9yczogc3RyaW5nW10pOiBzdHJpbmcge1xuICBjb25zdCB1bmlxdWVFcnJvcnMgPSBbLi4ubmV3IFNldChlcnJvcnMpXTtcbiAgcmV0dXJuIFtcbiAgICAnR2F0ZWtlZXBlciBwb2xpY3kgdmFsaWRhdGlvbiBmYWlsZWQ6JyxcbiAgICAuLi51bmlxdWVFcnJvcnMubWFwKGVycm9yID0+IGAgIOKAoiAke2Vycm9yfWApLFxuICBdLmpvaW4oJ1xcbicpO1xufVxuLy8gRGVsZWdhdGUgdG8gc2hhcmVkIG5vcm1hbGl6YXRpb24gdXRpbGl0eSAoSXNzdWUgIzQzMylcbmNvbnN0IEVMRU1FTlRfVFlQRV9BTElBU0VTID0gRUxFTUVOVF9UWVBFX01BUDtcblxuY29uc3QgRUxFTUVOVF9UWVBFX0xBQkVMUzogUmVjb3JkPEVsZW1lbnRUeXBlLCB7IHNpbmd1bGFyOiBzdHJpbmc7IHBsdXJhbDogc3RyaW5nIH0+ID0ge1xuICBbRWxlbWVudFR5cGUuUEVSU09OQV06IHsgc2luZ3VsYXI6ICdwZXJzb25hJywgcGx1cmFsOiAncGVyc29uYXMnIH0sXG4gIFtFbGVtZW50VHlwZS5TS0lMTF06IHsgc2luZ3VsYXI6ICdza2lsbCcsIHBsdXJhbDogJ3NraWxscycgfSxcbiAgW0VsZW1lbnRUeXBlLlRFTVBMQVRFXTogeyBzaW5ndWxhcjogJ3RlbXBsYXRlJywgcGx1cmFsOiAndGVtcGxhdGVzJyB9LFxuICBbRWxlbWVudFR5cGUuQUdFTlRdOiB7IHNpbmd1bGFyOiAnYWdlbnQnLCBwbHVyYWw6ICdhZ2VudHMnIH0sXG4gIFtFbGVtZW50VHlwZS5NRU1PUlldOiB7IHNpbmd1bGFyOiAnbWVtb3J5JywgcGx1cmFsOiAnbWVtb3JpZXMnIH0sXG4gIFtFbGVtZW50VHlwZS5FTlNFTUJMRV06IHsgc2luZ3VsYXI6ICdlbnNlbWJsZScsIHBsdXJhbDogJ2Vuc2VtYmxlcycgfVxufTtcblxuZXhwb3J0IGZ1bmN0aW9uIG5vcm1hbGl6ZUVsZW1lbnRUeXBlSW5wdXQocmF3VHlwZTogc3RyaW5nIHwgbnVsbCB8IHVuZGVmaW5lZCk6IHsgdHlwZTogRWxlbWVudFR5cGUgfCBudWxsOyBhbGlhc1VzZWQ6IGJvb2xlYW4gfSB7XG4gIGlmICghcmF3VHlwZSB8fCB0eXBlb2YgcmF3VHlwZSAhPT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4geyB0eXBlOiBudWxsLCBhbGlhc1VzZWQ6IGZhbHNlIH07XG4gIH1cblxuICBjb25zdCBub3JtYWxpemVkS2V5ID0gcmF3VHlwZS50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgY29uc3QgcmVzb2x2ZWRUeXBlID0gRUxFTUVOVF9UWVBFX0FMSUFTRVNbbm9ybWFsaXplZEtleV07XG5cbiAgaWYgKCFyZXNvbHZlZFR5cGUpIHtcbiAgICByZXR1cm4geyB0eXBlOiBudWxsLCBhbGlhc1VzZWQ6IGZhbHNlIH07XG4gIH1cblxuICBjb25zdCBhbGlhc1VzZWQgPSAhQUxMX0VMRU1FTlRfVFlQRVMuaW5jbHVkZXMocmF3VHlwZSBhcyBFbGVtZW50VHlwZSkgJiYgbm9ybWFsaXplZEtleSAhPT0gcmVzb2x2ZWRUeXBlO1xuICBpZiAoYWxpYXNVc2VkKSB7XG4gICAgbG9nZ2VyLndhcm4oYFVzaW5nIHNpbmd1bGFyIGVsZW1lbnQgdHlwZSAnJHtyYXdUeXBlfScgaXMgZGVwcmVjYXRlZC4gUGxlYXNlIHVzZSAnJHtyZXNvbHZlZFR5cGV9JyBpbnN0ZWFkLmApO1xuICB9XG5cbiAgcmV0dXJuIHsgdHlwZTogcmVzb2x2ZWRUeXBlLCBhbGlhc1VzZWQgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFZhbGlkRWxlbWVudFR5cGVzTGlzdCgpOiBzdHJpbmcge1xuICByZXR1cm4gc2hhcmVkRm9ybWF0RWxlbWVudFR5cGVzTGlzdCgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudFR5cGVMYWJlbCh0eXBlOiBFbGVtZW50VHlwZSwgb3B0aW9uczogeyBwbHVyYWw/OiBib29sZWFuIH0gPSB7fSk6IHN0cmluZyB7XG4gIGNvbnN0IGxhYmVsID0gRUxFTUVOVF9UWVBFX0xBQkVMU1t0eXBlXTtcbiAgaWYgKCFsYWJlbCkge1xuICAgIHJldHVybiBvcHRpb25zLnBsdXJhbCA/IHR5cGUgOiB0eXBlLnJlcGxhY2UoL3MkLywgJycpO1xuICB9XG4gIHJldHVybiBvcHRpb25zLnBsdXJhbCA/IGxhYmVsLnBsdXJhbCA6IGxhYmVsLnNpbmd1bGFyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudEZpbGVuYW1lKHR5cGU6IEVsZW1lbnRUeXBlLCBuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBzYWZlU2x1ZyA9IHNsdWdpZnkobmFtZSB8fCAnJykgfHwgJ3VudGl0bGVkJztcbiAgY29uc3QgZXh0ZW5zaW9uID0gdHlwZSA9PT0gRWxlbWVudFR5cGUuTUVNT1JZID8gJy55YW1sJyA6ICcubWQnO1xuICByZXR1cm4gYCR7c2FmZVNsdWd9JHtleHRlbnNpb259YDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBFbGVtZW50TWFuYWdlck9wZXJhdGlvbnM8VD4ge1xuICBsaXN0PzogKCkgPT4gUHJvbWlzZTxUW10+O1xuICBmaW5kPzogKHByZWRpY2F0ZTogKGNhbmRpZGF0ZTogVCkgPT4gYm9vbGVhbikgPT4gUHJvbWlzZTxUIHwgdW5kZWZpbmVkPjtcbiAgbG9hZD86IChmaWxlUGF0aDogc3RyaW5nKSA9PiBQcm9taXNlPFQ+O1xufVxuXG4vKipcbiAqIEtub3duIG1ldGFkYXRhIHByb3BlcnRpZXMgZm9yIGVhY2ggZWxlbWVudCB0eXBlLlxuICogVXNlZCBieSBib3RoIGBkZXRlY3RVbmtub3duTWV0YWRhdGFQcm9wZXJ0aWVzKClgIChjcmVhdGUvZWRpdCB3YXJuaW5ncylcbiAqIGFuZCBgZWRpdEVsZW1lbnQoKWAgKGZpZWxkIHJvdXRpbmcgdG8gbWV0YWRhdGEgdnMgZWxlbWVudCBvYmplY3QpLlxuICpcbiAqIElNUE9SVEFOVDogQm90aCBjYW1lbENhc2UgYW5kIHNuYWtlX2Nhc2UgdmFyaWFudHMgbXVzdCBiZSBsaXN0ZWQgd2hlcmVcbiAqIGFwcGxpY2FibGUsIHNpbmNlIExMTXMgbWF5IHNlbmQgZWl0aGVyIGZvcm0uIFRoZSBsb29rdXAgdXNlcyBleGFjdFxuICogc3RyaW5nIG1hdGNoaW5nIHZpYSBgU2V0LmhhcygpYC5cbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gQ29tbW9uIHByb3BlcnRpZXMgc2hhcmVkIGFjcm9zcyBhbGwgdHlwZXM6XG4gKiAnbmFtZScsICdkZXNjcmlwdGlvbicsICdhdXRob3InLCAndmVyc2lvbicsICd0YWdzJywgJ2dhdGVrZWVwZXInXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIFR5cGUtc3BlY2lmaWMgcHJvcGVydGllcyAob25seSB2YWxpZCBmb3IgdGhhdCBlbGVtZW50IHR5cGUpOlxuICogQWdlbnQ6ICdnb2FsJywgJ2FjdGl2YXRlcycsICd0b29scycsICdhdXRvbm9teScsICdyZXNpbGllbmNlJ1xuICogU2tpbGw6ICdkb21haW5zJywgJ2NhdGVnb3J5JywgJ3ByZXJlcXVpc2l0ZXMnXG4gKiBFbnNlbWJsZTogJ2VsZW1lbnRzJywgJ2FjdGl2YXRpb25TdHJhdGVneScsICdhY3RpdmF0aW9uX3N0cmF0ZWd5J1xuICovXG5leHBvcnQgY29uc3QgS05PV05fTUVUQURBVEFfUFJPUEVSVElFUzogUmVjb3JkPEVsZW1lbnRUeXBlLCBTZXQ8c3RyaW5nPj4gPSB7XG4gIFtFbGVtZW50VHlwZS5QRVJTT05BXTogbmV3IFNldChbXG4gICAgLy8gQ29tbW9uIG1ldGFkYXRhXG4gICAgJ25hbWUnLCAnZGVzY3JpcHRpb24nLCAnYXV0aG9yJywgJ3ZlcnNpb24nLCAndGFncycsICdjcmVhdGVkJywgJ21vZGlmaWVkJyxcbiAgICAvLyBHYXRla2VlcGVyIHBvbGljeSAoYWxsIGVsZW1lbnQgdHlwZXMpXG4gICAgJ2dhdGVrZWVwZXInLFxuICAgIC8vIFBlcnNvbmEtc3BlY2lmaWNcbiAgICAndHJpZ2dlcnMnLCAndG9uZScsICd2b2ljZScsICdkb21haW4nLCAnY29udGV4dCcsICdzdHlsZScsICdpbnN0cnVjdGlvbnMnLFxuICAgICdwZXJzb25hbGl0eScsICdleHBlcnRpc2UnLCAnY29tbXVuaWNhdGlvbl9zdHlsZScsICdjb21tdW5pY2F0aW9uU3R5bGUnXG4gIF0pLFxuICBbRWxlbWVudFR5cGUuU0tJTExdOiBuZXcgU2V0KFtcbiAgICAvLyBDb21tb24gbWV0YWRhdGFcbiAgICAnbmFtZScsICdkZXNjcmlwdGlvbicsICdhdXRob3InLCAndmVyc2lvbicsICd0YWdzJywgJ2NyZWF0ZWQnLCAnbW9kaWZpZWQnLFxuICAgIC8vIEdhdGVrZWVwZXIgcG9saWN5IChhbGwgZWxlbWVudCB0eXBlcylcbiAgICAnZ2F0ZWtlZXBlcicsXG4gICAgLy8gU2tpbGwtc3BlY2lmaWNcbiAgICAndHJpZ2dlcnMnLCAnZG9tYWluJywgJ2RvbWFpbnMnLCAnY2F0ZWdvcnknLCAnZXhhbXBsZXMnLCAncHJlcmVxdWlzaXRlcycsXG4gICAgJ2NvbnRlbnQnLCAnaW5zdHJ1Y3Rpb25zJywgJ3VzYWdlJywgJ2NhcGFiaWxpdGllcydcbiAgXSksXG4gIFtFbGVtZW50VHlwZS5URU1QTEFURV06IG5ldyBTZXQoW1xuICAgIC8vIENvbW1vbiBtZXRhZGF0YVxuICAgICduYW1lJywgJ2Rlc2NyaXB0aW9uJywgJ2F1dGhvcicsICd2ZXJzaW9uJywgJ3RhZ3MnLCAnY3JlYXRlZCcsICdtb2RpZmllZCcsXG4gICAgLy8gR2F0ZWtlZXBlciBwb2xpY3kgKGFsbCBlbGVtZW50IHR5cGVzKVxuICAgICdnYXRla2VlcGVyJyxcbiAgICAvLyBUZW1wbGF0ZS1zcGVjaWZpY1xuICAgICd2YXJpYWJsZXMnLCAndGVtcGxhdGUnLCAnY2F0ZWdvcnknLCAnb3V0cHV0Rm9ybWF0JywgJ291dHB1dF9mb3JtYXQnLFxuICAgICdjb250ZW50JywgJ2luc3RydWN0aW9ucycsICdmb3JtYXQnLCAnc2NoZW1hJ1xuICBdKSxcbiAgW0VsZW1lbnRUeXBlLkFHRU5UXTogbmV3IFNldChbXG4gICAgLy8gQ29tbW9uIG1ldGFkYXRhXG4gICAgJ25hbWUnLCAnZGVzY3JpcHRpb24nLCAnYXV0aG9yJywgJ3ZlcnNpb24nLCAndGFncycsICdjcmVhdGVkJywgJ21vZGlmaWVkJyxcbiAgICAvLyBHYXRla2VlcGVyIHBvbGljeSAoYWxsIGVsZW1lbnQgdHlwZXMpXG4gICAgJ2dhdGVrZWVwZXInLFxuICAgIC8vIEFnZW50IFYxIGZpZWxkcyAobGVnYWN5LCBzdGlsbCBzdXBwb3J0ZWQpXG4gICAgJ2dvYWxzJywgJ2NvbnN0cmFpbnRzJywgJ2NhcGFiaWxpdGllcycsICd0cmlnZ2VycycsICdzdGF0ZScsICdhY3Rpb25zJyxcbiAgICAnZGVjaXNpb25GcmFtZXdvcmsnLCAnZGVjaXNpb25fZnJhbWV3b3JrJywgJ3NraWxscycsXG4gICAgLy8gQWdlbnQgVjIgZmllbGRzICh2Mi4wLjAgQWdlbnRpYyBMb29wIFJlZGVzaWduKVxuICAgICdnb2FsJywgJ2FjdGl2YXRlcycsICd0b29scycsICdzeXN0ZW1Qcm9tcHQnLCAnc3lzdGVtX3Byb21wdCcsXG4gICAgJ2F1dG9ub215JywgJ3Jlc2lsaWVuY2UnLCAnc3VjY2Vzc0NyaXRlcmlhJywgJ3N1Y2Nlc3NfY3JpdGVyaWEnLFxuICAgIC8vIElzc3VlICM1ODU6IEJvZHkgY29udGVudCBmaWVsZHMgKHN0b3JlZCBpbiBleHRlbnNpb25zLmluc3RydWN0aW9ucylcbiAgICAnY29udGVudCcsICdpbnN0cnVjdGlvbnMnLFxuICAgIC8vIFYxIGJhY2t3YXJkIGNvbXBhdGliaWxpdHlcbiAgICAnc3BlY2lhbGl6YXRpb25zJywgJ3Jpc2tUb2xlcmFuY2UnLCAncmlza190b2xlcmFuY2UnLFxuICAgICdsZWFybmluZ0VuYWJsZWQnLCAnbGVhcm5pbmdfZW5hYmxlZCcsICdtYXhDb25jdXJyZW5