@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.
137 lines • 16.4 kB
JavaScript
/**
* Utility functions for Memory element operations
*
* PERFORMANCE CONSIDERATIONS:
* - ID generation should be fast (<1ms)
* - Hash functions optimized for memory verification
* - Index operations designed for O(log n) complexity
*
* SECURITY CONSIDERATIONS:
* - Hash functions normalize Unicode to prevent bypass attempts
* - Content verification detects external modifications
* - Protects against manual editing and tampering
*/
import * as crypto from 'crypto';
import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
import { SecurityMonitor } from '../../security/securityMonitor.js';
import { MEMORY_SECURITY_EVENTS } from './constants.js';
/**
* Generate a unique ID for memory entries
* Format: mem_{timestamp}_{random}
*
* @returns Unique memory entry ID
* @example
* generateMemoryId() // "mem_1699234567890_x7k2n9p4m"
*/
export function generateMemoryId() {
const timestamp = Date.now();
const random = Math.random().toString(36).substr(2, 9);
return `mem_${timestamp}_${random}`;
}
/**
* Generate a content hash for memory integrity verification
* Uses SHA-256 for cryptographic strength with Unicode normalization
*
* SECURITY: Normalizes content to prevent Unicode-based bypass attempts
* where attackers use homographs or invisible characters to create
* content that appears identical but has a different hash
*
* @param content - The content to hash (will be normalized)
* @returns Hex-encoded hash string
*/
export function generateContentHash(content) {
// SECURITY FIX: Normalize Unicode to prevent homograph attacks
// This ensures consistent hashing regardless of Unicode representation
const normalized = UnicodeValidator.normalize(content);
if (!normalized.isValid && normalized.detectedIssues) {
// Log potential security issue but still generate hash
// We want to detect the tampering, not reject it silently
SecurityMonitor.logSecurityEvent({
type: MEMORY_SECURITY_EVENTS.MEMORY_UNICODE_VALIDATION_FAILED,
severity: 'MEDIUM',
source: 'generateContentHash',
details: `Unicode security issues detected: ${normalized.detectedIssues.join(', ')}`
});
}
return crypto.createHash('sha256')
.update(normalized.normalizedContent)
.digest('hex');
}
/**
* Verify memory content integrity
*
* SECURITY: This function detects:
* - External tool modifications (didn't use our hash function)
* - Manual human editing of YAML files
* - Filesystem corruption
* - Tampering attempts (including Unicode tricks)
*
* @param content - The content to verify (will be normalized)
* @param expectedHash - The expected hash value
* @returns True if content matches hash after normalization
*/
export function verifyContentIntegrity(content, expectedHash) {
const actualHash = generateContentHash(content);
const isValid = actualHash === expectedHash;
if (!isValid) {
// SECURITY: Log integrity violation for audit trail
// This could indicate external modification or tampering
SecurityMonitor.logSecurityEvent({
type: MEMORY_SECURITY_EVENTS.MEMORY_INTEGRITY_VIOLATION,
severity: 'HIGH',
source: 'verifyContentIntegrity',
details: `Hash mismatch detected. Expected: ${expectedHash.substring(0, 8)}..., Got: ${actualHash.substring(0, 8)}...`
});
}
return isValid;
}
/**
* Calculate shard key for memory distribution
* Used for distributing memories across multiple files
*
* SECURITY: Normalizes input to ensure consistent sharding
* regardless of Unicode representation
*
* @param memoryId - The memory ID (will be normalized)
* @param shardCount - Number of shards (default 16)
* @returns Shard index (0 to shardCount-1)
*/
export function calculateShardKey(memoryId, shardCount = 16) {
// SECURITY FIX: Normalize memoryId to prevent Unicode-based attacks
// that could cause memories to be placed in unexpected shards
const normalized = UnicodeValidator.normalize(memoryId);
if (!normalized.isValid && normalized.detectedIssues) {
// Log but continue - we still need to calculate a shard
SecurityMonitor.logSecurityEvent({
type: MEMORY_SECURITY_EVENTS.MEMORY_UNICODE_VALIDATION_FAILED,
severity: 'LOW',
source: 'calculateShardKey',
details: `Unicode issues in memory ID: ${normalized.detectedIssues.join(', ')}`
});
}
const hash = crypto.createHash('md5')
.update(normalized.normalizedContent)
.digest();
const hashInt = hash.readUInt32BE(0);
return hashInt % shardCount;
}
/**
* Parse memory ID to extract timestamp
*
* @param memoryId - The memory ID to parse
* @returns Timestamp or null if invalid format
*/
export function parseMemoryTimestamp(memoryId) {
const match = memoryId.match(/^mem_(\d+)_/);
if (match && match[1]) {
const timestamp = Number.parseInt(match[1], 10);
return Number.isNaN(timestamp) ? null : timestamp;
}
return null;
}
// TODO: Future index utilities
// - createMemoryIndex(): Create B-tree or similar structure for fast lookups
// - updateIndex(): Update index when memories are added/removed
// - searchIndex(): O(log n) search through indexed memories
// - mergeIndices(): Combine multiple index files for distributed search
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvbWVtb3JpZXMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7OztHQVlHO0FBRUgsT0FBTyxLQUFLLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDakMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDakYsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXhEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCO0lBQzlCLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUM3QixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDdkQsT0FBTyxPQUFPLFNBQVMsSUFBSSxNQUFNLEVBQUUsQ0FBQztBQUN0QyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxPQUFlO0lBQ2pELCtEQUErRDtJQUMvRCx1RUFBdUU7SUFDdkUsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRXZELElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxJQUFJLFVBQVUsQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUNyRCx1REFBdUQ7UUFDdkQsMERBQTBEO1FBQzFELGVBQWUsQ0FBQyxnQkFBZ0IsQ0FBQztZQUMvQixJQUFJLEVBQUUsc0JBQXNCLENBQUMsZ0NBQWdDO1lBQzdELFFBQVEsRUFBRSxRQUFRO1lBQ2xCLE1BQU0sRUFBRSxxQkFBcUI7WUFDN0IsT0FBTyxFQUFF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b3J5IGVsZW1lbnQgb3BlcmF0aW9uc1xuICpcbiAqIFBFUkZPUk1BTkNFIENPTlNJREVSQVRJT05TOlxuICogLSBJRCBnZW5lcmF0aW9uIHNob3VsZCBiZSBmYXN0ICg8MW1zKVxuICogLSBIYXNoIGZ1bmN0aW9ucyBvcHRpbWl6ZWQgZm9yIG1lbW9yeSB2ZXJpZmljYXRpb25cbiAqIC0gSW5kZXggb3BlcmF0aW9ucyBkZXNpZ25lZCBmb3IgTyhsb2cgbikgY29tcGxleGl0eVxuICpcbiAqIFNFQ1VSSVRZIENPTlNJREVSQVRJT05TOlxuICogLSBIYXNoIGZ1bmN0aW9ucyBub3JtYWxpemUgVW5pY29kZSB0byBwcmV2ZW50IGJ5cGFzcyBhdHRlbXB0c1xuICogLSBDb250ZW50IHZlcmlmaWNhdGlvbiBkZXRlY3RzIGV4dGVybmFsIG1vZGlmaWNhdGlvbnNcbiAqIC0gUHJvdGVjdHMgYWdhaW5zdCBtYW51YWwgZWRpdGluZyBhbmQgdGFtcGVyaW5nXG4gKi9cblxuaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5TW9uaXRvciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3NlY3VyaXR5TW9uaXRvci5qcyc7XG5pbXBvcnQgeyBNRU1PUllfU0VDVVJJVFlfRVZFTlRTIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuXG4vKipcbiAqIEdlbmVyYXRlIGEgdW5pcXVlIElEIGZvciBtZW1vcnkgZW50cmllc1xuICogRm9ybWF0OiBtZW1fe3RpbWVzdGFtcH1fe3JhbmRvbX1cbiAqXG4gKiBAcmV0dXJucyBVbmlxdWUgbWVtb3J5IGVudHJ5IElEXG4gKiBAZXhhbXBsZVxuICogZ2VuZXJhdGVNZW1vcnlJZCgpIC8vIFwibWVtXzE2OTkyMzQ1Njc4OTBfeDdrMm45cDRtXCJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlTWVtb3J5SWQoKTogc3RyaW5nIHtcbiAgY29uc3QgdGltZXN0YW1wID0gRGF0ZS5ub3coKTtcbiAgY29uc3QgcmFuZG9tID0gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDkpO1xuICByZXR1cm4gYG1lbV8ke3RpbWVzdGFtcH1fJHtyYW5kb219YDtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZSBhIGNvbnRlbnQgaGFzaCBmb3IgbWVtb3J5IGludGVncml0eSB2ZXJpZmljYXRpb25cbiAqIFVzZXMgU0hBLTI1NiBmb3IgY3J5cHRvZ3JhcGhpYyBzdHJlbmd0aCB3aXRoIFVuaWNvZGUgbm9ybWFsaXphdGlvblxuICpcbiAqIFNFQ1VSSVRZOiBOb3JtYWxpemVzIGNvbnRlbnQgdG8gcHJldmVudCBVbmljb2RlLWJhc2VkIGJ5cGFzcyBhdHRlbXB0c1xuICogd2hlcmUgYXR0YWNrZXJzIHVzZSBob21vZ3JhcGhzIG9yIGludmlzaWJsZSBjaGFyYWN0ZXJzIHRvIGNyZWF0ZVxuICogY29udGVudCB0aGF0IGFwcGVhcnMgaWRlbnRpY2FsIGJ1dCBoYXMgYSBkaWZmZXJlbnQgaGFzaFxuICpcbiAqIEBwYXJhbSBjb250ZW50IC0gVGhlIGNvbnRlbnQgdG8gaGFzaCAod2lsbCBiZSBub3JtYWxpemVkKVxuICogQHJldHVybnMgSGV4LWVuY29kZWQgaGFzaCBzdHJpbmdcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlQ29udGVudEhhc2goY29udGVudDogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gU0VDVVJJVFkgRklYOiBOb3JtYWxpemUgVW5pY29kZSB0byBwcmV2ZW50IGhvbW9ncmFwaCBhdHRhY2tzXG4gIC8vIFRoaXMgZW5zdXJlcyBjb25zaXN0ZW50IGhhc2hpbmcgcmVnYXJkbGVzcyBvZiBVbmljb2RlIHJlcHJlc2VudGF0aW9uXG4gIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShjb250ZW50KTtcblxuICBpZiAoIW5vcm1hbGl6ZWQuaXNWYWxpZCAmJiBub3JtYWxpemVkLmRldGVjdGVkSXNzdWVzKSB7XG4gICAgLy8gTG9nIHBvdGVudGlhbCBzZWN1cml0eSBpc3N1ZSBidXQgc3RpbGwgZ2VuZXJhdGUgaGFzaFxuICAgIC8vIFdlIHdhbnQgdG8gZGV0ZWN0IHRoZSB0YW1wZXJpbmcsIG5vdCByZWplY3QgaXQgc2lsZW50bHlcbiAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICB0eXBlOiBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLk1FTU9SWV9VTklDT0RFX1ZBTElEQVRJT05fRkFJTEVELFxuICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgc291cmNlOiAnZ2VuZXJhdGVDb250ZW50SGFzaCcsXG4gICAgICBkZXRhaWxzOiBgVW5pY29kZSBzZWN1cml0eSBpc3N1ZXMgZGV0ZWN0ZWQ6ICR7bm9ybWFsaXplZC5kZXRlY3RlZElzc3Vlcy5qb2luKCcsICcpfWBcbiAgICB9KTtcbiAgfVxuXG4gIHJldHVybiBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMjU2JylcbiAgICAudXBkYXRlKG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQpXG4gICAgLmRpZ2VzdCgnaGV4Jyk7XG59XG5cbi8qKlxuICogVmVyaWZ5IG1lbW9yeSBjb250ZW50IGludGVncml0eVxuICpcbiAqIFNFQ1VSSVRZOiBUaGlzIGZ1bmN0aW9uIGRldGVjdHM6XG4gKiAtIEV4dGVybmFsIHRvb2wgbW9kaWZpY2F0aW9ucyAoZGlkbid0IHVzZSBvdXIgaGFzaCBmdW5jdGlvbilcbiAqIC0gTWFudWFsIGh1bWFuIGVkaXRpbmcgb2YgWUFNTCBmaWxlc1xuICogLSBGaWxlc3lzdGVtIGNvcnJ1cHRpb25cbiAqIC0gVGFtcGVyaW5nIGF0dGVtcHRzIChpbmNsdWRpbmcgVW5pY29kZSB0cmlja3MpXG4gKlxuICogQHBhcmFtIGNvbnRlbnQgLSBUaGUgY29udGVudCB0byB2ZXJpZnkgKHdpbGwgYmUgbm9ybWFsaXplZClcbiAqIEBwYXJhbSBleHBlY3RlZEhhc2ggLSBUaGUgZXhwZWN0ZWQgaGFzaCB2YWx1ZVxuICogQHJldHVybnMgVHJ1ZSBpZiBjb250ZW50IG1hdGNoZXMgaGFzaCBhZnRlciBub3JtYWxpemF0aW9uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2ZXJpZnlDb250ZW50SW50ZWdyaXR5KGNvbnRlbnQ6IHN0cmluZywgZXhwZWN0ZWRIYXNoOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3QgYWN0dWFsSGFzaCA9IGdlbmVyYXRlQ29udGVudEhhc2goY29udGVudCk7XG4gIGNvbnN0IGlzVmFsaWQgPSBhY3R1YWxIYXNoID09PSBleHBlY3RlZEhhc2g7XG5cbiAgaWYgKCFpc1ZhbGlkKSB7XG4gICAgLy8gU0VDVVJJVFk6IExvZyBpbnRlZ3JpdHkgdmlvbGF0aW9uIGZvciBhdWRpdCB0cmFpbFxuICAgIC8vIFRoaXMgY291bGQgaW5kaWNhdGUgZXh0ZXJuYWwgbW9kaWZpY2F0aW9uIG9yIHRhbXBlcmluZ1xuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX0lOVEVHUklUWV9WSU9MQVRJT04sXG4gICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgc291cmNlOiAndmVyaWZ5Q29udGVudEludGVncml0eScsXG4gICAgICBkZXRhaWxzOiBgSGFzaCBtaXNtYXRjaCBkZXRlY3RlZC4gRXhwZWN0ZWQ6ICR7ZXhwZWN0ZWRIYXNoLnN1YnN0cmluZygwLCA4KX0uLi4sIEdvdDogJHthY3R1YWxIYXNoLnN1YnN0cmluZygwLCA4KX0uLi5gXG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gaXNWYWxpZDtcbn1cblxuLyoqXG4gKiBDYWxjdWxhdGUgc2hhcmQga2V5IGZvciBtZW1vcnkgZGlzdHJpYnV0aW9uXG4gKiBVc2VkIGZvciBkaXN0cmlidXRpbmcgbWVtb3JpZXMgYWNyb3NzIG11bHRpcGxlIGZpbGVzXG4gKlxuICogU0VDVVJJVFk6IE5vcm1hbGl6ZXMgaW5wdXQgdG8gZW5zdXJlIGNvbnNpc3RlbnQgc2hhcmRpbmdcbiAqIHJlZ2FyZGxlc3Mgb2YgVW5pY29kZSByZXByZXNlbnRhdGlvblxuICpcbiAqIEBwYXJhbSBtZW1vcnlJZCAtIFRoZSBtZW1vcnkgSUQgKHdpbGwgYmUgbm9ybWFsaXplZClcbiAqIEBwYXJhbSBzaGFyZENvdW50IC0gTnVtYmVyIG9mIHNoYXJkcyAoZGVmYXVsdCAxNilcbiAqIEByZXR1cm5zIFNoYXJkIGluZGV4ICgwIHRvIHNoYXJkQ291bnQtMSlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZVNoYXJkS2V5KG1lbW9yeUlkOiBzdHJpbmcsIHNoYXJkQ291bnQgPSAxNik6IG51bWJlciB7XG4gIC8vIFNFQ1VSSVRZIEZJWDogTm9ybWFsaXplIG1lbW9yeUlkIHRvIHByZXZlbnQgVW5pY29kZS1iYXNlZCBhdHRhY2tzXG4gIC8vIHRoYXQgY291bGQgY2F1c2UgbWVtb3JpZXMgdG8gYmUgcGxhY2VkIGluIHVuZXhwZWN0ZWQgc2hhcmRzXG4gIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShtZW1vcnlJZCk7XG5cbiAgaWYgKCFub3JtYWxpemVkLmlzVmFsaWQgJiYgbm9ybWFsaXplZC5kZXRlY3RlZElzc3Vlcykge1xuICAgIC8vIExvZyBidXQgY29udGludWUgLSB3ZSBzdGlsbCBuZWVkIHRvIGNhbGN1bGF0ZSBhIHNoYXJkXG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogTUVNT1JZX1NFQ1VSSVRZX0VWRU5UUy5NRU1PUllfVU5JQ09ERV9WQUxJREFUSU9OX0ZBSUxFRCxcbiAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgIHNvdXJjZTogJ2NhbGN1bGF0ZVNoYXJkS2V5JyxcbiAgICAgIGRldGFpbHM6IGBVbmljb2RlIGlzc3VlcyBpbiBtZW1vcnkgSUQ6ICR7bm9ybWFsaXplZC5kZXRlY3RlZElzc3Vlcy5qb2luKCcsICcpfWBcbiAgICB9KTtcbiAgfVxuXG4gIGNvbnN0IGhhc2ggPSBjcnlwdG8uY3JlYXRlSGFzaCgnbWQ1JylcbiAgICAudXBkYXRlKG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQpXG4gICAgLmRpZ2VzdCgpO1xuICBjb25zdCBoYXNoSW50ID0gaGFzaC5yZWFkVUludDMyQkUoMCk7XG4gIHJldHVybiBoYXNoSW50ICUgc2hhcmRDb3VudDtcbn1cblxuLyoqXG4gKiBQYXJzZSBtZW1vcnkgSUQgdG8gZXh0cmFjdCB0aW1lc3RhbXBcbiAqXG4gKiBAcGFyYW0gbWVtb3J5SWQgLSBUaGUgbWVtb3J5IElEIHRvIHBhcnNlXG4gKiBAcmV0dXJucyBUaW1lc3RhbXAgb3IgbnVsbCBpZiBpbnZhbGlkIGZvcm1hdFxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VNZW1vcnlUaW1lc3RhbXAobWVtb3J5SWQ6IHN0cmluZyk6IG51bWJlciB8IG51bGwge1xuICBjb25zdCBtYXRjaCA9IG1lbW9yeUlkLm1hdGNoKC9ebWVtXyhcXGQrKV8vKTtcbiAgaWYgKG1hdGNoICYmIG1hdGNoWzFdKSB7XG4gICAgY29uc3QgdGltZXN0YW1wID0gTnVtYmVyLnBhcnNlSW50KG1hdGNoWzFdLCAxMCk7XG4gICAgcmV0dXJuIE51bWJlci5pc05hTih0aW1lc3RhbXApID8gbnVsbCA6IHRpbWVzdGFtcDtcbiAgfVxuICByZXR1cm4gbnVsbDtcbn1cblxuLy8gVE9ETzogRnV0dXJlIGluZGV4IHV0aWxpdGllc1xuLy8gLSBjcmVhdGVNZW1vcnlJbmRleCgpOiBDcmVhdGUgQi10cmVlIG9yIHNpbWlsYXIgc3RydWN0dXJlIGZvciBmYXN0IGxvb2t1cHNcbi8vIC0gdXBkYXRlSW5kZXgoKTogVXBkYXRlIGluZGV4IHdoZW4gbWVtb3JpZXMgYXJlIGFkZGVkL3JlbW92ZWRcbi8vIC0gc2VhcmNoSW5kZXgoKTogTyhsb2cgbikgc2VhcmNoIHRocm91Z2ggaW5kZXhlZCBtZW1vcmllc1xuLy8gLSBtZXJnZUluZGljZXMoKTogQ29tYmluZSBtdWx0aXBsZSBpbmRleCBmaWxlcyBmb3IgZGlzdHJpYnV0ZWQgc2VhcmNoIl19