@dollhousemcp/mcp-server 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +118 -0
- package/README.github.md +126 -8
- package/README.md +1 -1
- package/README.md.backup +50 -795
- package/README.npm.md +1 -1
- package/dist/collection/CollectionBrowser.d.ts.map +1 -1
- package/dist/collection/CollectionBrowser.js +6 -5
- package/dist/config/ConfigWizardDisplay.d.ts +64 -0
- package/dist/config/ConfigWizardDisplay.d.ts.map +1 -0
- package/dist/config/ConfigWizardDisplay.js +150 -0
- package/dist/config/WizardFirstResponse.d.ts +25 -0
- package/dist/config/WizardFirstResponse.d.ts.map +1 -0
- package/dist/config/WizardFirstResponse.js +118 -0
- package/dist/elements/memories/Memory.d.ts +190 -0
- package/dist/elements/memories/Memory.d.ts.map +1 -0
- package/dist/elements/memories/Memory.js +627 -0
- package/dist/elements/memories/MemoryManager.d.ts +136 -0
- package/dist/elements/memories/MemoryManager.d.ts.map +1 -0
- package/dist/elements/memories/MemoryManager.js +607 -0
- package/dist/elements/memories/MemorySearchIndex.d.ts +156 -0
- package/dist/elements/memories/MemorySearchIndex.d.ts.map +1 -0
- package/dist/elements/memories/MemorySearchIndex.js +690 -0
- package/dist/elements/memories/constants.d.ts +95 -0
- package/dist/elements/memories/constants.d.ts.map +1 -0
- package/dist/elements/memories/constants.js +102 -0
- package/dist/elements/memories/index.d.ts +7 -0
- package/dist/elements/memories/index.d.ts.map +1 -0
- package/dist/elements/memories/index.js +7 -0
- package/dist/elements/memories/utils.d.ts +68 -0
- package/dist/elements/memories/utils.d.ts.map +1 -0
- package/dist/elements/memories/utils.js +137 -0
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/scripts/scripts/run-config-wizard.js +57 -0
- package/dist/scripts/src/config/ConfigManager.js +799 -0
- package/dist/scripts/src/config/ConfigWizard.js +368 -0
- package/dist/scripts/src/errors/SecurityError.js +47 -0
- package/dist/scripts/src/security/constants.js +28 -0
- package/dist/scripts/src/security/contentValidator.js +415 -0
- package/dist/scripts/src/security/errors.js +32 -0
- package/dist/scripts/src/security/regexValidator.js +217 -0
- package/dist/scripts/src/security/secureYamlParser.js +272 -0
- package/dist/scripts/src/security/securityMonitor.js +111 -0
- package/dist/scripts/src/security/validators/unicodeValidator.js +315 -0
- package/dist/scripts/src/utils/logger.js +288 -0
- package/dist/security/audit/SecurityAuditor.d.ts.map +1 -1
- package/dist/security/audit/SecurityAuditor.js +24 -2
- package/dist/security/audit/config/suppressions.d.ts.map +1 -1
- package/dist/security/audit/config/suppressions.js +91 -1
- package/dist/security/securityMonitor.d.ts +1 -1
- package/dist/security/securityMonitor.d.ts.map +1 -1
- package/dist/security/securityMonitor.js +1 -1
- package/dist/tools/getWelcomeMessage.d.ts +41 -0
- package/dist/tools/getWelcomeMessage.d.ts.map +1 -0
- package/dist/tools/getWelcomeMessage.js +109 -0
- package/package.json +1 -1
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryManager - Implementation of IElementManager for Memory elements
|
|
3
|
+
* Handles CRUD operations and lifecycle management for memories implementing IElement
|
|
4
|
+
*
|
|
5
|
+
* FIXES IMPLEMENTED:
|
|
6
|
+
* 1. CRITICAL: Fixed race conditions in file operations by using FileLockManager for atomic reads/writes
|
|
7
|
+
* 2. HIGH: Fixed unvalidated YAML parsing vulnerability by using SecureYamlParser
|
|
8
|
+
* 3. MEDIUM: All user inputs are now validated and sanitized
|
|
9
|
+
* 4. MEDIUM: Audit logging added for security operations
|
|
10
|
+
* 5. MEDIUM: Path validation prevents directory traversal attacks
|
|
11
|
+
*/
|
|
12
|
+
import { Memory } from './Memory.js';
|
|
13
|
+
import { ElementType } from '../../portfolio/types.js';
|
|
14
|
+
import { PortfolioManager } from '../../portfolio/PortfolioManager.js';
|
|
15
|
+
import { FileLockManager } from '../../security/fileLockManager.js';
|
|
16
|
+
import { SecureYamlParser } from '../../security/secureYamlParser.js';
|
|
17
|
+
import { SecurityMonitor } from '../../security/securityMonitor.js';
|
|
18
|
+
import { sanitizeInput } from '../../security/InputValidator.js';
|
|
19
|
+
import { MEMORY_CONSTANTS, MEMORY_SECURITY_EVENTS } from './constants.js';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import * as fs from 'fs/promises';
|
|
22
|
+
import * as yaml from 'js-yaml';
|
|
23
|
+
import * as crypto from 'crypto';
|
|
24
|
+
export class MemoryManager {
|
|
25
|
+
portfolioManager;
|
|
26
|
+
memoriesDir;
|
|
27
|
+
memoryCache = new Map();
|
|
28
|
+
contentHashIndex = new Map();
|
|
29
|
+
// PERFORMANCE IMPROVEMENT: Cache for date folders to avoid directory scanning
|
|
30
|
+
// Invalidated when new folders are created
|
|
31
|
+
dateFoldersCache = null;
|
|
32
|
+
dateFoldersCacheTimestamp = 0;
|
|
33
|
+
constructor() {
|
|
34
|
+
this.portfolioManager = PortfolioManager.getInstance();
|
|
35
|
+
this.memoriesDir = this.portfolioManager.getElementDir(ElementType.MEMORY);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load a memory from file
|
|
39
|
+
* SECURITY FIX #1: Uses FileLockManager.atomicReadFile() instead of fs.readFile()
|
|
40
|
+
* to prevent race conditions and ensure atomic file operations
|
|
41
|
+
* @param filePath Path to the memory file to load
|
|
42
|
+
* @returns Promise resolving to the loaded Memory instance
|
|
43
|
+
* @throws {Error} When file cannot be found or path validation fails
|
|
44
|
+
* @throws {Error} When YAML parsing fails or content is malformed
|
|
45
|
+
* @throws {Error} When memory validation fails after loading
|
|
46
|
+
*/
|
|
47
|
+
async load(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
let fullPath;
|
|
50
|
+
// Check if it's a relative path (no date folder)
|
|
51
|
+
if (!filePath.includes(path.sep) || !filePath.match(/^\d{4}-\d{2}-\d{2}/)) {
|
|
52
|
+
// Search in date folders
|
|
53
|
+
const dateFolders = await this.getDateFolders();
|
|
54
|
+
let found = false;
|
|
55
|
+
for (const dateFolder of dateFolders) {
|
|
56
|
+
const testPath = path.join(this.memoriesDir, dateFolder, filePath);
|
|
57
|
+
if (await fs.access(testPath).then(() => true).catch(() => false)) {
|
|
58
|
+
fullPath = testPath;
|
|
59
|
+
found = true;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!found) {
|
|
64
|
+
// Fall back to root directory for backward compatibility during transition
|
|
65
|
+
fullPath = await this.validateAndResolvePath(filePath);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
fullPath = await this.validateAndResolvePath(filePath);
|
|
70
|
+
}
|
|
71
|
+
// Ensure fullPath is defined
|
|
72
|
+
if (!fullPath) {
|
|
73
|
+
throw new Error(`Could not resolve path: ${filePath}`);
|
|
74
|
+
}
|
|
75
|
+
// Check cache first
|
|
76
|
+
const cached = this.memoryCache.get(fullPath);
|
|
77
|
+
if (cached) {
|
|
78
|
+
return cached;
|
|
79
|
+
}
|
|
80
|
+
// CRITICAL FIX: Use atomic file read to prevent race conditions
|
|
81
|
+
// Previously: const content = await fs.readFile(fullPath, 'utf-8');
|
|
82
|
+
// Now: Uses FileLockManager with proper encoding object format
|
|
83
|
+
const content = await FileLockManager.atomicReadFile(fullPath, { encoding: 'utf-8' });
|
|
84
|
+
// HIGH SEVERITY FIX: Use SecureYamlParser to prevent YAML injection attacks
|
|
85
|
+
// Previously: Could use unsafe YAML parsing
|
|
86
|
+
// Now: Uses SecureYamlParser which validates content and prevents malicious patterns
|
|
87
|
+
const parsed = SecureYamlParser.parse(content, {
|
|
88
|
+
maxYamlSize: MEMORY_CONSTANTS.MAX_YAML_SIZE,
|
|
89
|
+
validateContent: true
|
|
90
|
+
});
|
|
91
|
+
// Extract metadata and content
|
|
92
|
+
const { metadata, content: memoryContent } = this.parseMemoryFile(parsed);
|
|
93
|
+
// Create memory instance
|
|
94
|
+
const memory = new Memory(metadata);
|
|
95
|
+
// Load saved entries if present
|
|
96
|
+
if (parsed.data && parsed.data.entries) {
|
|
97
|
+
memory.deserialize(JSON.stringify({
|
|
98
|
+
id: memory.id,
|
|
99
|
+
type: memory.type,
|
|
100
|
+
version: memory.version,
|
|
101
|
+
metadata: memory.metadata,
|
|
102
|
+
extensions: memory.extensions,
|
|
103
|
+
entries: parsed.data.entries
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
// Cache the loaded memory
|
|
107
|
+
this.memoryCache.set(fullPath, memory);
|
|
108
|
+
// Log successful load
|
|
109
|
+
SecurityMonitor.logSecurityEvent({
|
|
110
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_LOADED,
|
|
111
|
+
severity: 'LOW',
|
|
112
|
+
source: 'MemoryManager.load',
|
|
113
|
+
details: `Loaded memory from ${path.basename(fullPath)}`
|
|
114
|
+
});
|
|
115
|
+
return memory;
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
SecurityMonitor.logSecurityEvent({
|
|
119
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_LOAD_FAILED,
|
|
120
|
+
severity: 'MEDIUM',
|
|
121
|
+
source: 'MemoryManager.load',
|
|
122
|
+
details: `Failed to load memory from ${filePath}: ${error}`
|
|
123
|
+
});
|
|
124
|
+
throw new Error(`Failed to load memory: ${error}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Generate date-based path for memory storage
|
|
129
|
+
* Creates YYYY-MM-DD folder structure to prevent flat directory issues
|
|
130
|
+
* @param element Memory element to save
|
|
131
|
+
* @param fileName Optional custom filename
|
|
132
|
+
* @returns Full path to memory file
|
|
133
|
+
*/
|
|
134
|
+
async generateMemoryPath(element, fileName) {
|
|
135
|
+
const date = new Date();
|
|
136
|
+
const dateFolder = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
137
|
+
const datePath = path.join(this.memoriesDir, dateFolder);
|
|
138
|
+
// Ensure date folder exists
|
|
139
|
+
await fs.mkdir(datePath, { recursive: true });
|
|
140
|
+
// PERFORMANCE IMPROVEMENT: Invalidate date folders cache since we created a new folder
|
|
141
|
+
this.dateFoldersCache = null;
|
|
142
|
+
// Generate filename
|
|
143
|
+
const baseName = fileName || `${element.metadata.name?.toLowerCase().replace(/\s+/g, '-') || 'memory'}.yaml`;
|
|
144
|
+
let finalName = baseName;
|
|
145
|
+
let version = 1;
|
|
146
|
+
// Handle collisions with version suffix
|
|
147
|
+
while (await fs.access(path.join(datePath, finalName)).then(() => true).catch(() => false)) {
|
|
148
|
+
version++;
|
|
149
|
+
finalName = baseName.replace('.yaml', `-v${version}.yaml`);
|
|
150
|
+
}
|
|
151
|
+
return path.join(datePath, finalName);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Calculate SHA-256 hash of memory content for deduplication
|
|
155
|
+
* Implements Issue #994 - Content-based deduplication
|
|
156
|
+
*/
|
|
157
|
+
calculateContentHash(element) {
|
|
158
|
+
const content = JSON.stringify({
|
|
159
|
+
metadata: element.metadata,
|
|
160
|
+
entries: JSON.parse(element.serialize()).entries
|
|
161
|
+
});
|
|
162
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get all date folders in memories directory
|
|
166
|
+
* PERFORMANCE IMPROVEMENT: Uses cache to avoid repeated directory scanning
|
|
167
|
+
* Cache is invalidated when new folders are created or after 60 seconds
|
|
168
|
+
* @returns Array of date folder names
|
|
169
|
+
*/
|
|
170
|
+
async getDateFolders() {
|
|
171
|
+
const now = Date.now();
|
|
172
|
+
const CACHE_TTL = 60000; // 60 seconds
|
|
173
|
+
// Return cached result if valid
|
|
174
|
+
if (this.dateFoldersCache !== null &&
|
|
175
|
+
(now - this.dateFoldersCacheTimestamp) < CACHE_TTL) {
|
|
176
|
+
return this.dateFoldersCache;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const entries = await fs.readdir(this.memoriesDir, { withFileTypes: true });
|
|
180
|
+
const folders = entries
|
|
181
|
+
.filter(entry => entry.isDirectory() && /^\d{4}-\d{2}-\d{2}$/.test(entry.name))
|
|
182
|
+
.map(entry => entry.name)
|
|
183
|
+
.sort()
|
|
184
|
+
.reverse(); // Most recent first
|
|
185
|
+
// Cache the result
|
|
186
|
+
this.dateFoldersCache = folders;
|
|
187
|
+
this.dateFoldersCacheTimestamp = now;
|
|
188
|
+
return folders;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (error.code === 'ENOENT') {
|
|
192
|
+
// Cache empty result
|
|
193
|
+
this.dateFoldersCache = [];
|
|
194
|
+
this.dateFoldersCacheTimestamp = now;
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Save a memory to file
|
|
202
|
+
* SECURITY FIX #1: Uses FileLockManager.atomicWriteFile() for atomic operations
|
|
203
|
+
* @param element Memory element to save
|
|
204
|
+
* @param filePath Optional custom file path, defaults to date-based path
|
|
205
|
+
* @returns Promise that resolves when save is complete
|
|
206
|
+
* @throws {Error} When memory validation fails before saving
|
|
207
|
+
* @throws {Error} When path validation fails or file system errors occur
|
|
208
|
+
* @throws {Error} When atomic write operation fails
|
|
209
|
+
*/
|
|
210
|
+
async save(element, filePath) {
|
|
211
|
+
try {
|
|
212
|
+
// Validate element
|
|
213
|
+
const validation = element.validate();
|
|
214
|
+
if (!validation.valid) {
|
|
215
|
+
throw new Error(`Invalid memory: ${validation.errors?.map(e => e.message).join(', ')}`);
|
|
216
|
+
}
|
|
217
|
+
// Calculate content hash for deduplication
|
|
218
|
+
const contentHash = this.calculateContentHash(element);
|
|
219
|
+
const existingPath = this.contentHashIndex.get(contentHash);
|
|
220
|
+
if (existingPath) {
|
|
221
|
+
// Log duplicate detection
|
|
222
|
+
SecurityMonitor.logSecurityEvent({
|
|
223
|
+
type: 'MEMORY_DUPLICATE_DETECTED',
|
|
224
|
+
severity: 'LOW',
|
|
225
|
+
source: 'MemoryManager.save',
|
|
226
|
+
details: `Duplicate content detected. Existing: ${existingPath}`
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// Generate date-based path if not provided
|
|
230
|
+
const fullPath = filePath
|
|
231
|
+
? await this.validateAndResolvePath(filePath)
|
|
232
|
+
: await this.generateMemoryPath(element);
|
|
233
|
+
// Ensure parent directory exists (for date folders)
|
|
234
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
235
|
+
// Get memory statistics
|
|
236
|
+
const stats = element.getStats();
|
|
237
|
+
// Prepare data for saving
|
|
238
|
+
const data = {
|
|
239
|
+
metadata: element.metadata,
|
|
240
|
+
extensions: element.extensions,
|
|
241
|
+
stats: {
|
|
242
|
+
totalEntries: stats.totalEntries,
|
|
243
|
+
totalSize: stats.totalSize,
|
|
244
|
+
oldestEntry: stats.oldestEntry?.toISOString(),
|
|
245
|
+
newestEntry: stats.newestEntry?.toISOString(),
|
|
246
|
+
topTags: Array.from(stats.tagFrequency.entries())
|
|
247
|
+
.sort((a, b) => b[1] - a[1])
|
|
248
|
+
.slice(0, 10)
|
|
249
|
+
.map(([tag, count]) => ({ tag, count }))
|
|
250
|
+
},
|
|
251
|
+
entries: JSON.parse(element.serialize()).entries
|
|
252
|
+
};
|
|
253
|
+
// SECURITY FIX: Use secure YAML dumping with safety options
|
|
254
|
+
// Previously: Could allow dangerous YAML features
|
|
255
|
+
// Now: Uses FAILSAFE_SCHEMA and security options to prevent code execution
|
|
256
|
+
const yamlContent = yaml.dump(data, {
|
|
257
|
+
schema: yaml.FAILSAFE_SCHEMA,
|
|
258
|
+
noRefs: true,
|
|
259
|
+
skipInvalid: true,
|
|
260
|
+
sortKeys: true
|
|
261
|
+
});
|
|
262
|
+
// CRITICAL FIX: Use atomic file write to prevent corruption
|
|
263
|
+
// Previously: await fs.writeFile(fullPath, yamlContent, 'utf-8');
|
|
264
|
+
// Now: Uses FileLockManager for atomic write with proper encoding
|
|
265
|
+
await FileLockManager.atomicWriteFile(fullPath, yamlContent, { encoding: 'utf-8' });
|
|
266
|
+
// Update cache
|
|
267
|
+
this.memoryCache.set(fullPath, element);
|
|
268
|
+
// Update content hash index
|
|
269
|
+
this.contentHashIndex.set(contentHash, fullPath);
|
|
270
|
+
// Log successful save
|
|
271
|
+
SecurityMonitor.logSecurityEvent({
|
|
272
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_SAVED,
|
|
273
|
+
severity: 'LOW',
|
|
274
|
+
source: 'MemoryManager.save',
|
|
275
|
+
details: `Saved memory to ${path.basename(fullPath)} with ${stats.totalEntries} entries`
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
SecurityMonitor.logSecurityEvent({
|
|
280
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_SAVE_FAILED,
|
|
281
|
+
severity: 'HIGH',
|
|
282
|
+
source: 'MemoryManager.save',
|
|
283
|
+
details: `Failed to save memory to ${filePath}: ${error}`
|
|
284
|
+
});
|
|
285
|
+
throw new Error(`Failed to save memory: ${error}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* List all available memories
|
|
290
|
+
*/
|
|
291
|
+
async list() {
|
|
292
|
+
const memories = [];
|
|
293
|
+
try {
|
|
294
|
+
// Get all date folders
|
|
295
|
+
const dateFolders = await this.getDateFolders();
|
|
296
|
+
// Also check root directory for any legacy files
|
|
297
|
+
const rootFiles = await fs.readdir(this.memoriesDir)
|
|
298
|
+
.then(files => files.filter(f => f.endsWith('.yaml')))
|
|
299
|
+
.catch(() => []);
|
|
300
|
+
// Process root files first (legacy)
|
|
301
|
+
for (const file of rootFiles) {
|
|
302
|
+
try {
|
|
303
|
+
const memory = await this.load(file);
|
|
304
|
+
memories.push(memory);
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
SecurityMonitor.logSecurityEvent({
|
|
308
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_LIST_ITEM_FAILED,
|
|
309
|
+
severity: 'LOW',
|
|
310
|
+
source: 'MemoryManager.list',
|
|
311
|
+
details: `Failed to load ${file}: ${error}`
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Process date folders
|
|
316
|
+
for (const dateFolder of dateFolders) {
|
|
317
|
+
const folderPath = path.join(this.memoriesDir, dateFolder);
|
|
318
|
+
const files = await fs.readdir(folderPath)
|
|
319
|
+
.then(files => files.filter(f => f.endsWith('.yaml')))
|
|
320
|
+
.catch(() => []);
|
|
321
|
+
for (const file of files) {
|
|
322
|
+
try {
|
|
323
|
+
const memory = await this.load(path.join(dateFolder, file));
|
|
324
|
+
memories.push(memory);
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
SecurityMonitor.logSecurityEvent({
|
|
328
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_LIST_ITEM_FAILED,
|
|
329
|
+
severity: 'LOW',
|
|
330
|
+
source: 'MemoryManager.list',
|
|
331
|
+
details: `Failed to load ${dateFolder}/${file}: ${error}`
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return memories;
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
if (error.code === 'ENOENT') {
|
|
340
|
+
// Directory doesn't exist yet
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Find memories matching a predicate
|
|
348
|
+
*/
|
|
349
|
+
async find(predicate) {
|
|
350
|
+
const memories = await this.list();
|
|
351
|
+
return memories.find(predicate);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Find multiple memories matching a predicate
|
|
355
|
+
*/
|
|
356
|
+
async findMany(predicate) {
|
|
357
|
+
const memories = await this.list();
|
|
358
|
+
return memories.filter(predicate);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Delete a memory file
|
|
362
|
+
* SECURITY: Validates path and logs deletion
|
|
363
|
+
*/
|
|
364
|
+
async delete(filePath) {
|
|
365
|
+
try {
|
|
366
|
+
const fullPath = await this.validateAndResolvePath(filePath);
|
|
367
|
+
// Check if file exists
|
|
368
|
+
await fs.access(fullPath);
|
|
369
|
+
// Delete the file
|
|
370
|
+
await fs.unlink(fullPath);
|
|
371
|
+
// Remove from cache
|
|
372
|
+
this.memoryCache.delete(fullPath);
|
|
373
|
+
SecurityMonitor.logSecurityEvent({
|
|
374
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_DELETED,
|
|
375
|
+
severity: 'MEDIUM',
|
|
376
|
+
source: 'MemoryManager.delete',
|
|
377
|
+
details: `Deleted memory file: ${path.basename(fullPath)}`
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
if (error.code === 'ENOENT') {
|
|
382
|
+
// File doesn't exist, not an error for delete operation
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
throw error;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Check if a memory file exists
|
|
390
|
+
*/
|
|
391
|
+
async exists(filePath) {
|
|
392
|
+
try {
|
|
393
|
+
const fullPath = await this.validateAndResolvePath(filePath);
|
|
394
|
+
await fs.access(fullPath);
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Create a new memory with metadata
|
|
403
|
+
*/
|
|
404
|
+
async create(metadata) {
|
|
405
|
+
return new Memory(metadata);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Import a memory from JSON/YAML string
|
|
409
|
+
* SECURITY: Full validation of imported content
|
|
410
|
+
* @param data JSON or YAML string containing memory data
|
|
411
|
+
* @param format Format of the input data ('json' or 'yaml')
|
|
412
|
+
* @returns Promise resolving to the imported Memory instance
|
|
413
|
+
* @throws {Error} When JSON/YAML parsing fails
|
|
414
|
+
* @throws {Error} When imported data is missing required fields
|
|
415
|
+
* @throws {Error} When YAML content exceeds maximum allowed size
|
|
416
|
+
* @throws {Error} When imported memory fails validation
|
|
417
|
+
*/
|
|
418
|
+
async importElement(data, format = 'yaml') {
|
|
419
|
+
try {
|
|
420
|
+
let parsed;
|
|
421
|
+
if (format === 'json') {
|
|
422
|
+
parsed = JSON.parse(data);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
// HIGH SEVERITY FIX: Use secure YAML parsing
|
|
426
|
+
// Memory import expects pure YAML (not frontmatter), so we parse it securely
|
|
427
|
+
try {
|
|
428
|
+
// First validate the YAML content size
|
|
429
|
+
if (data.length > MEMORY_CONSTANTS.MAX_YAML_SIZE) {
|
|
430
|
+
throw new Error('YAML content exceeds maximum allowed size');
|
|
431
|
+
}
|
|
432
|
+
// Create a wrapper to use SecureYamlParser with pure YAML
|
|
433
|
+
// Add minimal frontmatter markers to satisfy parser
|
|
434
|
+
const wrappedYaml = `---\n${data}\n---\n`;
|
|
435
|
+
const parseResult = SecureYamlParser.parse(wrappedYaml, {
|
|
436
|
+
maxYamlSize: MEMORY_CONSTANTS.MAX_YAML_SIZE,
|
|
437
|
+
validateContent: true
|
|
438
|
+
});
|
|
439
|
+
// Extract the parsed data (will be in the 'data' property)
|
|
440
|
+
parsed = parseResult.data;
|
|
441
|
+
}
|
|
442
|
+
catch (yamlError) {
|
|
443
|
+
throw new Error(`Invalid YAML: ${yamlError}`);
|
|
444
|
+
}
|
|
445
|
+
// Validate it's an object
|
|
446
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
447
|
+
throw new Error('YAML must contain an object');
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Handle different structures from YAML parsing
|
|
451
|
+
let metadata = parsed.metadata;
|
|
452
|
+
let entries = parsed.entries || (parsed.data && parsed.data.entries);
|
|
453
|
+
// Validate required fields
|
|
454
|
+
if (!metadata || !metadata.name) {
|
|
455
|
+
throw new Error('Memory must have metadata with name');
|
|
456
|
+
}
|
|
457
|
+
// Create memory instance
|
|
458
|
+
const memory = new Memory(metadata);
|
|
459
|
+
// Load entries if present
|
|
460
|
+
if (entries) {
|
|
461
|
+
memory.deserialize(JSON.stringify({
|
|
462
|
+
id: memory.id,
|
|
463
|
+
type: memory.type,
|
|
464
|
+
version: memory.version,
|
|
465
|
+
metadata: memory.metadata,
|
|
466
|
+
extensions: memory.extensions,
|
|
467
|
+
entries: entries
|
|
468
|
+
}));
|
|
469
|
+
}
|
|
470
|
+
return memory;
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
SecurityMonitor.logSecurityEvent({
|
|
474
|
+
type: MEMORY_SECURITY_EVENTS.MEMORY_IMPORT_FAILED,
|
|
475
|
+
severity: 'MEDIUM',
|
|
476
|
+
source: 'MemoryManager.importElement',
|
|
477
|
+
details: `Failed to import memory: ${error}`
|
|
478
|
+
});
|
|
479
|
+
throw new Error(`Failed to import memory: ${error}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Export a memory to YAML string
|
|
484
|
+
*/
|
|
485
|
+
async exportElement(element) {
|
|
486
|
+
const stats = element.getStats();
|
|
487
|
+
const data = {
|
|
488
|
+
metadata: element.metadata,
|
|
489
|
+
extensions: element.extensions,
|
|
490
|
+
stats: {
|
|
491
|
+
totalEntries: stats.totalEntries,
|
|
492
|
+
totalSize: stats.totalSize,
|
|
493
|
+
oldestEntry: stats.oldestEntry?.toISOString(),
|
|
494
|
+
newestEntry: stats.newestEntry?.toISOString()
|
|
495
|
+
},
|
|
496
|
+
entries: JSON.parse(element.serialize()).entries
|
|
497
|
+
};
|
|
498
|
+
// SECURITY FIX: Use secure YAML dumping
|
|
499
|
+
return yaml.dump(data, {
|
|
500
|
+
schema: yaml.FAILSAFE_SCHEMA,
|
|
501
|
+
noRefs: true,
|
|
502
|
+
skipInvalid: true,
|
|
503
|
+
sortKeys: true
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Validate a memory element
|
|
508
|
+
*/
|
|
509
|
+
validate(element) {
|
|
510
|
+
return element.validate();
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Validate and resolve a file path
|
|
514
|
+
* SECURITY: Prevents directory traversal attacks
|
|
515
|
+
*/
|
|
516
|
+
validatePath(filePath) {
|
|
517
|
+
try {
|
|
518
|
+
// Perform synchronous validation checks
|
|
519
|
+
const normalized = path.normalize(filePath);
|
|
520
|
+
// Check for path traversal attempts
|
|
521
|
+
if (normalized.includes('..') || path.isAbsolute(normalized)) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
// Ensure proper extension
|
|
525
|
+
if (!normalized.endsWith('.md') && !normalized.endsWith('.yaml') && !normalized.endsWith('.yml')) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Get the element type this manager handles
|
|
536
|
+
*/
|
|
537
|
+
getElementType() {
|
|
538
|
+
return ElementType.MEMORY;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Get the file extension for memory files
|
|
542
|
+
*/
|
|
543
|
+
getFileExtension() {
|
|
544
|
+
return '.yaml';
|
|
545
|
+
}
|
|
546
|
+
// Private helper methods
|
|
547
|
+
/**
|
|
548
|
+
* Validate and resolve a file path to prevent security issues
|
|
549
|
+
* @param filePath Path to validate and resolve
|
|
550
|
+
* @returns Promise resolving to the validated full path
|
|
551
|
+
* @throws {Error} When path contains traversal attempts (../)
|
|
552
|
+
* @throws {Error} When path is absolute or invalid
|
|
553
|
+
* @throws {Error} When file extension is not allowed (.md, .yaml, .yml)
|
|
554
|
+
* @throws {Error} When resolved path would be outside memories directory
|
|
555
|
+
*/
|
|
556
|
+
async validateAndResolvePath(filePath) {
|
|
557
|
+
// SECURITY FIX: Comprehensive path validation
|
|
558
|
+
const normalized = path.normalize(filePath);
|
|
559
|
+
// Check for path traversal attempts
|
|
560
|
+
if (normalized.includes('..') || path.isAbsolute(normalized)) {
|
|
561
|
+
SecurityMonitor.logSecurityEvent({
|
|
562
|
+
type: 'PATH_TRAVERSAL_ATTEMPT',
|
|
563
|
+
severity: 'HIGH',
|
|
564
|
+
source: 'MemoryManager.validateAndResolvePath',
|
|
565
|
+
details: `Blocked path traversal attempt: ${filePath}`
|
|
566
|
+
});
|
|
567
|
+
throw new Error('Invalid file path: Path traversal detected');
|
|
568
|
+
}
|
|
569
|
+
// Ensure proper extension
|
|
570
|
+
if (!normalized.endsWith('.md') && !normalized.endsWith('.yaml') && !normalized.endsWith('.yml')) {
|
|
571
|
+
throw new Error('Memory files must have .md, .yaml, or .yml extension');
|
|
572
|
+
}
|
|
573
|
+
// Construct full path
|
|
574
|
+
const fullPath = path.join(this.memoriesDir, normalized);
|
|
575
|
+
// Verify it's within memories directory
|
|
576
|
+
const relative = path.relative(this.memoriesDir, fullPath);
|
|
577
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
578
|
+
throw new Error('File path must be within memories directory');
|
|
579
|
+
}
|
|
580
|
+
return fullPath;
|
|
581
|
+
}
|
|
582
|
+
parseMemoryFile(parsed) {
|
|
583
|
+
// Extract metadata with validation
|
|
584
|
+
const metadata = {
|
|
585
|
+
name: sanitizeInput(parsed.metadata?.name || 'Unnamed Memory', 100),
|
|
586
|
+
description: parsed.metadata?.description ?
|
|
587
|
+
sanitizeInput(parsed.metadata.description, 500) :
|
|
588
|
+
'',
|
|
589
|
+
version: parsed.metadata?.version || '1.0.0',
|
|
590
|
+
author: parsed.metadata?.author,
|
|
591
|
+
created: parsed.metadata?.created,
|
|
592
|
+
modified: new Date().toISOString(),
|
|
593
|
+
tags: Array.isArray(parsed.metadata?.tags) ?
|
|
594
|
+
parsed.metadata.tags.map((tag) => sanitizeInput(tag, MEMORY_CONSTANTS.MAX_TAG_LENGTH)) :
|
|
595
|
+
[],
|
|
596
|
+
storageBackend: parsed.metadata?.storageBackend || MEMORY_CONSTANTS.DEFAULT_STORAGE_BACKEND,
|
|
597
|
+
retentionDays: parsed.metadata?.retentionDays || MEMORY_CONSTANTS.DEFAULT_RETENTION_DAYS,
|
|
598
|
+
privacyLevel: parsed.metadata?.privacyLevel || MEMORY_CONSTANTS.DEFAULT_PRIVACY_LEVEL,
|
|
599
|
+
searchable: parsed.metadata?.searchable !== false,
|
|
600
|
+
maxEntries: parsed.metadata?.maxEntries || MEMORY_CONSTANTS.MAX_ENTRIES_DEFAULT
|
|
601
|
+
};
|
|
602
|
+
// Extract content (if any)
|
|
603
|
+
const content = parsed.content || '';
|
|
604
|
+
return { metadata, content };
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVtb3J5TWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9lbGVtZW50cy9tZW1vcmllcy9NZW1vcnlNYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7O0dBVUc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUErQixNQUFNLGFBQWEsQ0FBQztBQUdsRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDdkUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3RFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUVwRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDakUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLHNCQUFzQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDMUUsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxTQUFTLENBQUM7QUFDaEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFFakMsTUFBTSxPQUFPLGFBQWE7SUFDaEIsZ0JBQWdCLENBQW1CO0lBQ25DLFdBQVcsQ0FBUztJQUNwQixXQUFXLEdBQXdCLElBQUksR0FBRyxFQUFFLENBQUM7SUFDN0MsZ0JBQWdCLEdBQXdCLElBQUksR0FBRyxFQUFFLENBQUM7SUFFMUQsOEVBQThFO0lBQzlFLDJDQUEyQztJQUNuQyxnQkFBZ0IsR0FBb0IsSUFBSSxDQUFDO0lBQ3pDLHlCQUF5QixHQUFXLENBQUMsQ0FBQztJQUU5QztRQUNFLElBQUksQ0FBQyxn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LFFBQVEsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBVyxFQUFFLEVBQUUsQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLGdCQUFnQixDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDaEcsRUFBRTtZQUNKLGNBQWMsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLGNBQWMsSUFBSSxnQkFBZ0IsQ0FBQyx1QkFBdUI7WUFDM0YsYUFBYSxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsYUFBYSxJQUFJLGdCQUFnQixDQUFDLHNCQUFzQjtZQUN4RixZQUFZLEVBQUUsTUFBTSxDQUFDLFFBQVEsRUFBRSxZQUFZLElBQUksZ0JBQWdCLENBQUMscUJBQXFCO1lBQ3JGLFVBQVUsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLFVBQVUsS0FBSyxLQUFLO1lBQ2pELFVBQVUsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLFVBQVUsSUFBSSxnQkFBZ0IsQ0FBQyxtQkFBbUI7U0FDaEYsQ0FBQztRQUVGLDJCQUEyQjtRQUMzQixNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsT0FBTyxJQUFJLEVBQUUsQ0FBQztRQUVyQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDO0lBQy9CLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTWVtb3J5TWFuYWdlciAtIEltcGxlbWVudGF0aW9uIG9mIElFbGVtZW50TWFuYWdlciBmb3IgTWVtb3J5IGVsZW1lbnRzXG4gKiBIYW5kbGVzIENSVUQgb3BlcmF0aW9ucyBhbmQgbGlmZWN5Y2xlIG1hbmFnZW1lbnQgZm9yIG1lbW9yaWVzIGltcGxlbWVudGluZyBJRWxlbWVudFxuICogXG4gKiBGSVhFUyBJTVBMRU1FTlRFRDpcbiAqIDEuIENSSVRJQ0FMOiBGaXhlZCByYWNlIGNvbmRpdGlvbnMgaW4gZmlsZSBvcGVyYXRpb25zIGJ5IHVzaW5nIEZpbGVMb2NrTWFuYWdlciBmb3IgYXRvbWljIHJlYWRzL3dyaXRlc1xuICogMi4gSElHSDogRml4ZWQgdW52YWxpZGF0ZWQgWUFNTCBwYXJzaW5nIHZ1bG5lcmFiaWxpdHkgYnkgdXNpbmcgU2VjdXJlWWFtbFBhcnNlclxuICogMy4gTUVESVVNOiBBbGwgdXNlciBpbnB1dHMgYXJlIG5vdyB2YWxpZGF0ZWQgYW5kIHNhbml0aXplZFxuICogNC4gTUVESVVNOiBBdWRpdCBsb2dnaW5nIGFkZGVkIGZvciBzZWN1cml0eSBvcGVyYXRpb25zXG4gKiA1LiBNRURJVU06IFBhdGggdmFsaWRhdGlvbiBwcmV2ZW50cyBkaXJlY3RvcnkgdHJhdmVyc2FsIGF0dGFja3NcbiAqL1xuXG5pbXBvcnQgeyBNZW1vcnksIE1lbW9yeU1ldGFkYXRhLCBNZW1vcnlFbnRyeSB9IGZyb20gJy4vTWVtb3J5LmpzJztcbmltcG9ydCB7IElFbGVtZW50TWFuYWdlciB9IGZyb20gJy4uLy4uL3R5cGVzL2VsZW1lbnRzL0lFbGVtZW50TWFuYWdlci5qcyc7XG5pbXBvcnQgeyBFbGVtZW50VmFsaWRhdGlvblJlc3VsdCB9IGZyb20gJy4uLy4uL3R5cGVzL2VsZW1lbnRzL0lFbGVtZW50LmpzJztcbmltcG9ydCB7IEVsZW1lbnRUeXBlIH0gZnJvbSAnLi4vLi4vcG9ydGZvbGlvL3R5cGVzLmpzJztcbmltcG9ydCB7IFBvcnRmb2xpb01hbmFnZXIgfSBmcm9tICcuLi8uLi9wb3J0Zm9saW8vUG9ydGZvbGlvTWFuYWdlci5qcyc7XG5pbXBvcnQgeyBGaWxlTG9ja01hbmFnZXIgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9maWxlTG9ja01hbmFnZXIuanMnO1xuaW1wb3J0IHsgU2VjdXJlWWFtbFBhcnNlciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3NlY3VyZVlhbWxQYXJzZXIuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgc2FuaXRpemVJbnB1dCB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L0lucHV0VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IE1FTU9SWV9DT05TVEFOVFMsIE1FTU9SWV9TRUNVUklUWV9FVkVOVFMgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMvcHJvbWlzZXMnO1xuaW1wb3J0ICogYXMgeWFtbCBmcm9tICdqcy15YW1sJztcbmltcG9ydCAqIGFzIGNyeXB0byBmcm9tICdjcnlwdG8nO1xuXG5leHBvcnQgY2xhc3MgTWVtb3J5TWFuYWdlciBpbXBsZW1lbnRzIElFbGVtZW50TWFuYWdlcjxNZW1vcnk+IHtcbiAgcHJpdmF0ZSBwb3J0Zm9saW9NYW5hZ2VyOiBQb3J0Zm9saW9NYW5hZ2VyO1xuICBwcml2YXRlIG1lbW9yaWVzRGlyOiBzdHJpbmc7XG4gIHByaXZhdGUgbWVtb3J5Q2FjaGU6IE1hcDxzdHJpbmcsIE1lbW9yeT4gPSBuZXcgTWFwKCk7XG4gIHByaXZhdGUgY29udGVudEhhc2hJbmRleDogTWFwPHN0cmluZywgc3RyaW5nPiA9IG5ldyBNYXAoKTtcblxuICAvLyBQRVJGT1JNQU5DRSBJTVBST1ZFTUVOVDogQ2FjaGUgZm9yIGRhdGUgZm9sZGVycyB0byBhdm9pZCBkaXJlY3Rvcnkgc2Nhbm5pbmdcbiAgLy8gSW52YWxpZGF0ZWQgd2hlbiBuZXcgZm9sZGVycyBhcmUgY3JlYXRlZFxuICBwcml2YXRlIGRhdGVGb2xkZXJzQ2FjaGU6IHN0cmluZ1tdIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgZGF0ZUZvbGRlcnNDYWNoZVRpbWVzdGFtcDogbnVtYmVyID0gMDtcbiAgXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMucG9ydGZvbGlvTWFuYWdlciA9IFBvcnRmb2xpb01hbmFnZXIuZ2V0SW5zdGFuY2UoKTtcbiAgICB0aGlzLm1lbW9yaWVzRGlyID0gdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmdldEVsZW1lbnREaXIoRWxlbWVudFR5cGUuTUVNT1JZKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIExvYWQgYSBtZW1vcnkgZnJvbSBmaWxlXG4gICAqIFNFQ1VSSVRZIEZJWCAjMTogVXNlcyBGaWxlTG9ja01hbmFnZXIuYXRvbWljUmVhZEZpbGUoKSBpbnN0ZWFkIG9mIGZzLnJlYWRGaWxlKClcbiAgICogdG8gcHJldmVudCByYWNlIGNvbmRpdGlvbnMgYW5kIGVuc3VyZSBhdG9taWMgZmlsZSBvcGVyYXRpb25zXG4gICAqIEBwYXJhbSBmaWxlUGF0aCBQYXRoIHRvIHRoZSBtZW1vcnkgZmlsZSB0byBsb2FkXG4gICAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBsb2FkZWQgTWVtb3J5IGluc3RhbmNlXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBXaGVuIGZpbGUgY2Fubm90IGJlIGZvdW5kIG9yIHBhdGggdmFsaWRhdGlvbiBmYWlsc1xuICAgKiBAdGhyb3dzIHtFcnJvcn0gV2hlbiBZQU1MIHBhcnNpbmcgZmFpbHMgb3IgY29udGVudCBpcyBtYWxmb3JtZWRcbiAgICogQHRocm93cyB7RXJyb3J9IFdoZW4gbWVtb3J5IHZhbGlkYXRpb24gZmFpbHMgYWZ0ZXIgbG9hZGluZ1xuICAgKi9cbiAgYXN5bmMgbG9hZChmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTxNZW1vcnk+IHtcbiAgICB0cnkge1xuICAgICAgbGV0IGZ1bGxQYXRoOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgICAgIC8vIENoZWNrIGlmIGl0J3MgYSByZWxhdGl2ZSBwYXRoIChubyBkYXRlIGZvbGRlcilcbiAgICAgIGlmICghZmlsZVBhdGguaW5jbHVkZXMocGF0aC5zZXApIHx8ICFmaWxlUGF0aC5tYXRjaCgvXlxcZHs0fS1cXGR7Mn0tXFxkezJ9LykpIHtcbiAgICAgICAgLy8gU2VhcmNoIGluIGRhdGUgZm9sZGVyc1xuICAgICAgICBjb25zdCBkYXRlRm9sZGVycyA9IGF3YWl0IHRoaXMuZ2V0RGF0ZUZvbGRlcnMoKTtcbiAgICAgICAgbGV0IGZvdW5kID0gZmFsc2U7XG5cbiAgICAgICAgZm9yIChjb25zdCBkYXRlRm9sZGVyIG9mIGRhdGVGb2xkZXJzKSB7XG4gICAgICAgICAgY29uc3QgdGVzdFBhdGggPSBwYXRoLmpvaW4odGhpcy5tZW1vcmllc0RpciwgZGF0ZUZvbGRlciwgZmlsZVBhdGgpO1xuICAgICAgICAgIGlmIChhd2FpdCBmcy5hY2Nlc3ModGVzdFBhdGgpLnRoZW4oKCkgPT4gdHJ1ZSkuY2F0Y2goKCkgPT4gZmFsc2UpKSB7XG4gICAgICAgICAgICBmdWxsUGF0aCA9IHRlc3RQYXRoO1xuICAgICAgICAgICAgZm91bmQgPSB0cnVlO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFmb3VuZCkge1xuICAgICAgICAgIC8vIEZhbGwgYmFjayB0byByb290IGRpcmVjdG9yeSBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eSBkdXJpbmcgdHJhbnNpdGlvblxuICAgICAgICAgIGZ1bGxQYXRoID0gYXdhaXQgdGhpcy52YWxpZGF0ZUFuZFJlc29sdmVQYXRoKGZpbGVQYXRoKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZnVsbFBhdGggPSBhd2FpdCB0aGlzLnZhbGlkYXRlQW5kUmVzb2x2ZVBhdGgoZmlsZVBhdGgpO1xuICAgICAgfVxuXG4gICAgICAvLyBFbnN1cmUgZnVsbFBhdGggaXMgZGVmaW5lZFxuICAgICAgaWYgKCFmdWxsUGF0aCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCByZXNvbHZlIHBhdGg6ICR7ZmlsZVBhdGh9YCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGNhY2hlIGZpcnN0XG4gICAgICBjb25zdCBjYWNoZWQgPSB0aGlzLm1lbW9yeUNhY2hlLmdldChmdWxsUGF0aCk7XG4gICAgICBpZiAoY2FjaGVkKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWQ7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIENSSVRJQ0FMIEZJWDogVXNlIGF0b21pYyBmaWxlIHJlYWQgdG8gcHJldmVudCByYWNlIGNvbmRpdGlvbnNcbiAgICAgIC8vIFByZXZpb3VzbHk6IGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShmdWxsUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAvLyBOb3c6IFVzZXMgRmlsZUxvY2tNYW5hZ2VyIHdpdGggcHJvcGVyIGVuY29kaW5nIG9iamVjdCBmb3JtYXRcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBGaWxlTG9ja01hbmFnZXIuYXRvbWljUmVhZEZpbGUoZnVsbFBhdGgsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSk7XG4gICAgICBcbiAgICAgIC8vIEhJR0ggU0VWRVJJVFkgRklYOiBVc2UgU2VjdXJlWWFtbFBhcnNlciB0byBwcmV2ZW50IFlBTUwgaW5qZWN0aW9uIGF0dGFja3NcbiAgICAgIC8vIFByZXZpb3VzbHk6IENvdWxkIHVzZSB1bnNhZmUgWUFNTCBwYXJzaW5nXG4gICAgICAvLyBOb3c6IFVzZXMgU2VjdXJlWWFtbFBhcnNlciB3aGljaCB2YWxpZGF0ZXMgY29udGVudCBhbmQgcHJldmVudHMgbWFsaWNpb3VzIHBhdHRlcm5zXG4gICAgICBjb25zdCBwYXJzZWQgPSBTZWN1cmVZYW1sUGFyc2VyLnBhcnNlKGNvbnRlbnQsIHtcbiAgICAgICAgbWF4WWFtbFNpemU6IE1FTU9SWV9DT05TVEFOVFMuTUFYX1lBTUxfU0laRSxcbiAgICAgICAgdmFsaWRhdGVDb250ZW50OiB0cnVlXG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgLy8gRXh0cmFjdCBtZXRhZGF0YSBhbmQgY29udGVudFxuICAgICAgY29uc3QgeyBtZXRhZGF0YSwgY29udGVudDogbWVtb3J5Q29udGVudCB9ID0gdGhpcy5wYXJzZU1lbW9yeUZpbGUocGFyc2VkKTtcbiAgICAgIFxuICAgICAgLy8gQ3JlYXRlIG1lbW9yeSBpbnN0YW5jZVxuICAgICAgY29uc3QgbWVtb3J5ID0gbmV3IE1lbW9yeShtZXRhZGF0YSk7XG4gICAgICBcbiAgICAgIC8vIExvYWQgc2F2ZWQgZW50cmllcyBpZiBwcmVzZW50XG4gICAgICBpZiAocGFyc2VkLmRhdGEgJiYgcGFyc2VkLmRhdGEuZW50cmllcykge1xuICAgICAgICBtZW1vcnkuZGVzZXJpYWxpemUoSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgIGlkOiBtZW1vcnkuaWQsXG4gICAgICAgICAgdHlwZTogbWVtb3J5LnR5cGUsXG4gICAgICAgICAgdmVyc2lvbjogbWVtb3J5LnZlcnNpb24sXG4gICAgICAgICAgbWV0YWRhdGE6IG1lbW9yeS5tZXRhZGF0YSxcbiAgICAgICAgICBleHRlbnNpb25zOiBtZW1vcnkuZXh0ZW5zaW9ucyxcbiAgICAgICAgICBlbnRyaWVzOiBwYXJzZWQuZGF0YS5lbnRyaWVzXG4gICAgICAgIH0pKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQ2FjaGUgdGhlIGxvYWRlZCBtZW1vcnlcbiAgICAgIHRoaXMubWVtb3J5Q2FjaGUuc2V0KGZ1bGxQYXRoLCBtZW1vcnkpO1xuICAgICAgXG4gICAgICAvLyBMb2cgc3VjY2Vzc2Z1bCBsb2FkXG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX0xPQURFRCxcbiAgICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgICBzb3VyY2U6ICdNZW1vcnlNYW5hZ2VyLmxvYWQnLFxuICAgICAgICBkZXRhaWxzOiBgTG9hZGVkIG1lbW9yeSBmcm9tICR7cGF0aC5iYXNlbmFtZShmdWxsUGF0aCl9YFxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHJldHVybiBtZW1vcnk7XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLk1FTU9SWV9MT0FEX0ZBSUxFRCxcbiAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICBzb3VyY2U6ICdNZW1vcnlNYW5hZ2VyLmxvYWQnLFxuICAgICAgICBkZXRhaWxzOiBgRmFpbGVkIHRvIGxvYWQgbWVtb3J5IGZyb20gJHtmaWxlUGF0aH06ICR7ZXJyb3J9YFxuICAgICAgfSk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBsb2FkIG1lbW9yeTogJHtlcnJvcn1gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgZGF0ZS1iYXNlZCBwYXRoIGZvciBtZW1vcnkgc3RvcmFnZVxuICAgKiBDcmVhdGVzIFlZWVktTU0tREQgZm9sZGVyIHN0cnVjdHVyZSB0byBwcmV2ZW50IGZsYXQgZGlyZWN0b3J5IGlzc3Vlc1xuICAgKiBAcGFyYW0gZWxlbWVudCBNZW1vcnkgZWxlbWVudCB0byBzYXZlXG4gICAqIEBwYXJhbSBmaWxlTmFtZSBPcHRpb25hbCBjdXN0b20gZmlsZW5hbWVcbiAgICogQHJldHVybnMgRnVsbCBwYXRoIHRvIG1lbW9yeSBmaWxlXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdlbmVyYXRlTWVtb3J5UGF0aChlbGVtZW50OiBNZW1vcnksIGZpbGVOYW1lPzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoKTtcbiAgICBjb25zdCBkYXRlRm9sZGVyID0gYCR7ZGF0ZS5nZXRGdWxsWWVhcigpfS0ke1N0cmluZyhkYXRlLmdldE1vbnRoKCkgKyAxKS5wYWRTdGFydCgyLCAnMCcpfS0ke1N0cmluZyhkYXRlLmdldERhdGUoKSkucGFkU3RhcnQoMiwgJzAnKX1gO1xuICAgIGNvbnN0IGRhdGVQYXRoID0gcGF0aC5qb2luKHRoaXMubWVtb3JpZXNEaXIsIGRhdGVGb2xkZXIpO1xuXG4gICAgLy8gRW5zdXJlIGRhdGUgZm9sZGVyIGV4aXN0c1xuICAgIGF3YWl0IGZzLm1rZGlyKGRhdGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcblxuICAgIC8vIFBFUkZPUk1BTkNFIElNUFJPVkVNRU5UOiBJbnZhbGlkYXRlIGRhdGUgZm9sZGVycyBjYWNoZSBzaW5jZSB3ZSBjcmVhdGVkIGEgbmV3IGZvbGRlclxuICAgIHRoaXMuZGF0ZUZvbGRlcnNDYWNoZSA9IG51bGw7XG5cbiAgICAvLyBHZW5lcmF0ZSBmaWxlbmFtZVxuICAgIGNvbnN0IGJhc2VOYW1lID0gZmlsZU5hbWUgfHwgYCR7ZWxlbWVudC5tZXRhZGF0YS5uYW1lPy50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL1xccysvZywgJy0nKSB8fCAnbWVtb3J5J30ueWFtbGA7XG4gICAgbGV0IGZpbmFsTmFtZSA9IGJhc2VOYW1lO1xuICAgIGxldCB2ZXJzaW9uID0gMTtcblxuICAgIC8vIEhhbmRsZSBjb2xsaXNpb25zIHdpdGggdmVyc2lvbiBzdWZmaXhcbiAgICB3aGlsZSAoYXdhaXQgZnMuYWNjZXNzKHBhdGguam9pbihkYXRlUGF0aCwgZmluYWxOYW1lKSkudGhlbigoKSA9PiB0cnVlKS5jYXRjaCgoKSA9PiBmYWxzZSkpIHtcbiAgICAgIHZlcnNpb24rKztcbiAgICAgIGZpbmFsTmFtZSA9IGJhc2VOYW1lLnJlcGxhY2UoJy55YW1sJywgYC12JHt2ZXJzaW9ufS55YW1sYCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHBhdGguam9pbihkYXRlUGF0aCwgZmluYWxOYW1lKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgU0hBLTI1NiBoYXNoIG9mIG1lbW9yeSBjb250ZW50IGZvciBkZWR1cGxpY2F0aW9uXG4gICAqIEltcGxlbWVudHMgSXNzdWUgIzk5NCAtIENvbnRlbnQtYmFzZWQgZGVkdXBsaWNhdGlvblxuICAgKi9cbiAgcHJpdmF0ZSBjYWxjdWxhdGVDb250ZW50SGFzaChlbGVtZW50OiBNZW1vcnkpOiBzdHJpbmcge1xuICAgIGNvbnN0IGNvbnRlbnQgPSBKU09OLnN0cmluZ2lmeSh7XG4gICAgICBtZXRhZGF0YTogZWxlbWVudC5tZXRhZGF0YSxcbiAgICAgIGVudHJpZXM6IEpTT04ucGFyc2UoZWxlbWVudC5zZXJpYWxpemUoKSkuZW50cmllc1xuICAgIH0pO1xuICAgIHJldHVybiBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKGNvbnRlbnQpLmRpZ2VzdCgnaGV4Jyk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCBkYXRlIGZvbGRlcnMgaW4gbWVtb3JpZXMgZGlyZWN0b3J5XG4gICAqIFBFUkZPUk1BTkNFIElNUFJPVkVNRU5UOiBVc2VzIGNhY2hlIHRvIGF2b2lkIHJlcGVhdGVkIGRpcmVjdG9yeSBzY2FubmluZ1xuICAgKiBDYWNoZSBpcyBpbnZhbGlkYXRlZCB3aGVuIG5ldyBmb2xkZXJzIGFyZSBjcmVhdGVkIG9yIGFmdGVyIDYwIHNlY29uZHNcbiAgICogQHJldHVybnMgQXJyYXkgb2YgZGF0ZSBmb2xkZXIgbmFtZXNcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2V0RGF0ZUZvbGRlcnMoKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3QgQ0FDSEVfVFRMID0gNjAwMDA7IC8vIDYwIHNlY29uZHNcblxuICAgIC8vIFJldHVybiBjYWNoZWQgcmVzdWx0IGlmIHZhbGlkXG4gICAgaWYgKHRoaXMuZGF0ZUZvbGRlcnNDYWNoZSAhPT0gbnVsbCAmJlxuICAgICAgICAobm93IC0gdGhpcy5kYXRlRm9sZGVyc0NhY2hlVGltZXN0YW1wKSA8IENBQ0hFX1RUTCkge1xuICAgICAgcmV0dXJuIHRoaXMuZGF0ZUZvbGRlcnNDYWNoZTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgZW50cmllcyA9IGF3YWl0IGZzLnJlYWRkaXIodGhpcy5tZW1vcmllc0RpciwgeyB3aXRoRmlsZVR5cGVzOiB0cnVlIH0pO1xuICAgICAgY29uc3QgZm9sZGVycyA9IGVudHJpZXNcbiAgICAgICAgLmZpbHRlcihlbnRyeSA9PiBlbnRyeS5pc0RpcmVjdG9yeSgpICYmIC9eXFxkezR9LVxcZHsyfS1cXGR7Mn0kLy50ZXN0KGVudHJ5Lm5hbWUpKVxuICAgICAgICAubWFwKGVudHJ5ID0+IGVudHJ5Lm5hbWUpXG4gICAgICAgIC5zb3J0KClcbiAgICAgICAgLnJldmVyc2UoKTsgLy8gTW9zdCByZWNlbnQgZmlyc3RcblxuICAgICAgLy8gQ2FjaGUgdGhlIHJlc3VsdFxuICAgICAgdGhpcy5kYXRlRm9sZGVyc0NhY2hlID0gZm9sZGVycztcbiAgICAgIHRoaXMuZGF0ZUZvbGRlcnNDYWNoZVRpbWVzdGFtcCA9IG5vdztcblxuICAgICAgcmV0dXJuIGZvbGRlcnM7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlID09PSAnRU5PRU5UJykge1xuICAgICAgICAvLyBDYWNoZSBlbXB0eSByZXN1bHRcbiAgICAgICAgdGhpcy5kYXRlRm9sZGVyc0NhY2hlID0gW107XG4gICAgICAgIHRoaXMuZGF0ZUZvbGRlcnNDYWNoZVRpbWVzdGFtcCA9IG5vdztcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgfVxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNhdmUgYSBtZW1vcnkgdG8gZmlsZVxuICAgKiBTRUNVUklUWSBGSVggIzE6IFVzZXMgRmlsZUxvY2tNYW5hZ2VyLmF0b21pY1dyaXRlRmlsZSgpIGZvciBhdG9taWMgb3BlcmF0aW9uc1xuICAgKiBAcGFyYW0gZWxlbWVudCBNZW1vcnkgZWxlbWVudCB0byBzYXZlXG4gICAqIEBwYXJhbSBmaWxlUGF0aCBPcHRpb25hbCBjdXN0b20gZmlsZSBwYXRoLCBkZWZhdWx0cyB0byBkYXRlLWJhc2VkIHBhdGhcbiAgICogQHJldHVybnMgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gc2F2ZSBpcyBjb21wbGV0ZVxuICAgKiBAdGhyb3dzIHtFcnJvcn0gV2hlbiBtZW1vcnkgdmFsaWRhdGlvbiBmYWlscyBiZWZvcmUgc2F2aW5nXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBXaGVuIHBhdGggdmFsaWRhdGlvbiBmYWlscyBvciBmaWxlIHN5c3RlbSBlcnJvcnMgb2NjdXJcbiAgICogQHRocm93cyB7RXJyb3J9IFdoZW4gYXRvbWljIHdyaXRlIG9wZXJhdGlvbiBmYWlsc1xuICAgKi9cbiAgYXN5bmMgc2F2ZShlbGVtZW50OiBNZW1vcnksIGZpbGVQYXRoPzogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFZhbGlkYXRlIGVsZW1lbnRcbiAgICAgIGNvbnN0IHZhbGlkYXRpb24gPSBlbGVtZW50LnZhbGlkYXRlKCk7XG4gICAgICBpZiAoIXZhbGlkYXRpb24udmFsaWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIG1lbW9yeTogJHt2YWxpZGF0aW9uLmVycm9ycz8ubWFwKGUgPT4gZS5tZXNzYWdlKS5qb2luKCcsICcpfWApO1xuICAgICAgfVxuXG4gICAgICAvLyBDYWxjdWxhdGUgY29udGVudCBoYXNoIGZvciBkZWR1cGxpY2F0aW9uXG4gICAgICBjb25zdCBjb250ZW50SGFzaCA9IHRoaXMuY2FsY3VsYXRlQ29udGVudEhhc2goZWxlbWVudCk7XG4gICAgICBjb25zdCBleGlzdGluZ1BhdGggPSB0aGlzLmNvbnRlbnRIYXNoSW5kZXguZ2V0KGNvbnRlbnRIYXNoKTtcblxuICAgICAgaWYgKGV4aXN0aW5nUGF0aCkge1xuICAgICAgICAvLyBMb2cgZHVwbGljYXRlIGRldGVjdGlvblxuICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgdHlwZTogJ01FTU9SWV9EVVBMSUNBVEVfREVURUNURUQnLFxuICAgICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgICBzb3VyY2U6ICdNZW1vcnlNYW5hZ2VyLnNhdmUnLFxuICAgICAgICAgIGRldGFpbHM6IGBEdXBsaWNhdGUgY29udGVudCBkZXRlY3RlZC4gRXhpc3Rpbmc6ICR7ZXhpc3RpbmdQYXRofWBcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIEdlbmVyYXRlIGRhdGUtYmFzZWQgcGF0aCBpZiBub3QgcHJvdmlkZWRcbiAgICAgIGNvbnN0IGZ1bGxQYXRoID0gZmlsZVBhdGhcbiAgICAgICAgPyBhd2FpdCB0aGlzLnZhbGlkYXRlQW5kUmVzb2x2ZVBhdGgoZmlsZVBhdGgpXG4gICAgICAgIDogYXdhaXQgdGhpcy5nZW5lcmF0ZU1lbW9yeVBhdGgoZWxlbWVudCk7XG5cbiAgICAgIC8vIEVuc3VyZSBwYXJlbnQgZGlyZWN0b3J5IGV4aXN0cyAoZm9yIGRhdGUgZm9sZGVycylcbiAgICAgIGF3YWl0IGZzLm1rZGlyKHBhdGguZGlybmFtZShmdWxsUGF0aCksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgXG4gICAgICAvLyBHZXQgbWVtb3J5IHN0YXRpc3RpY3NcbiAgICAgIGNvbnN0IHN0YXRzID0gZWxlbWVudC5nZXRTdGF0cygpO1xuICAgICAgXG4gICAgICAvLyBQcmVwYXJlIGRhdGEgZm9yIHNhdmluZ1xuICAgICAgY29uc3QgZGF0YSA9IHtcbiAgICAgICAgbWV0YWRhdGE6IGVsZW1lbnQubWV0YWRhdGEsXG4gICAgICAgIGV4dGVuc2lvbnM6IGVsZW1lbnQuZXh0ZW5zaW9ucyxcbiAgICAgICAgc3RhdHM6IHtcbiAgICAgICAgICB0b3RhbEVudHJpZXM6IHN0YXRzLnRvdGFsRW50cmllcyxcbiAgICAgICAgICB0b3RhbFNpemU6IHN0YXRzLnRvdGFsU2l6ZSxcbiAgICAgICAgICBvbGRlc3RFbnRyeTogc3RhdHMub2xkZXN0RW50cnk/LnRvSVNPU3RyaW5nKCksXG4gICAgICAgICAgbmV3ZXN0RW50cnk6IHN0YXRzLm5ld2VzdEVudHJ5Py50b0lTT1N0cmluZygpLFxuICAgICAgICAgIHRvcFRhZ3M6IEFycmF5LmZyb20oc3RhdHMudGFnRnJlcXVlbmN5LmVudHJpZXMoKSlcbiAgICAgICAgICAgIC5zb3J0KChhLCBiKSA9PiBiWzFdIC0gYVsxXSlcbiAgICAgICAgICAgIC5zbGljZSgwLCAxMClcbiAgICAgICAgICAgIC5tYXAoKFt0YWcsIGNvdW50XSkgPT4gKHsgdGFnLCBjb3VudCB9KSlcbiAgICAgICAgfSxcbiAgICAgICAgZW50cmllczogSlNPTi5wYXJzZShlbGVtZW50LnNlcmlhbGl6ZSgpKS5lbnRyaWVzXG4gICAgICB9O1xuICAgICAgXG4gICAgICAvLyBTRUNVUklUWSBGSVg6IFVzZSBzZWN1cmUgWUFNTCBkdW1waW5nIHdpdGggc2FmZXR5IG9wdGlvbnNcbiAgICAgIC8vIFByZXZpb3VzbHk6IENvdWxkIGFsbG93IGRhbmdlcm91cyBZQU1MIGZlYXR1cmVzXG4gICAgICAvLyBOb3c6IFVzZXMgRkFJTFNBRkVfU0NIRU1BIGFuZCBzZWN1cml0eSBvcHRpb25zIHRvIHByZXZlbnQgY29kZSBleGVjdXRpb25cbiAgICAgIGNvbnN0IHlhbWxDb250ZW50ID0geWFtbC5kdW1wKGRhdGEsIHtcbiAgICAgICAgc2NoZW1hOiB5YW1sLkZBSUxTQUZFX1NDSEVNQSxcbiAgICAgICAgbm9SZWZzOiB0cnVlLFxuICAgICAgICBza2lwSW52YWxpZDogdHJ1ZSxcbiAgICAgICAgc29ydEtleXM6IHRydWVcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICAvLyBDUklUSUNBTCBGSVg6IFVzZSBhdG9taWMgZmlsZSB3cml0ZSB0byBwcmV2ZW50IGNvcnJ1cHRpb25cbiAgICAgIC8vIFByZXZpb3VzbHk6IGF3YWl0IGZzLndyaXRlRmlsZShmdWxsUGF0aCwgeWFtbENvbnRlbnQsICd1dGYtOCcpO1xuICAgICAgLy8gTm93OiBVc2VzIEZpbGVMb2NrTWFuYWdlciBmb3IgYXRvbWljIHdyaXRlIHdpdGggcHJvcGVyIGVuY29kaW5nXG4gICAgICBhd2FpdCBGaWxlTG9ja01hbmFnZXIuYXRvbWljV3JpdGVGaWxlKGZ1bGxQYXRoLCB5YW1sQ29udGVudCwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KTtcbiAgICAgIFxuICAgICAgLy8gVXBkYXRlIGNhY2hlXG4gICAgICB0aGlzLm1lbW9yeUNhY2hlLnNldChmdWxsUGF0aCwgZWxlbWVudCk7XG5cbiAgICAgIC8vIFVwZGF0ZSBjb250ZW50IGhhc2ggaW5kZXhcbiAgICAgIHRoaXMuY29udGVudEhhc2hJbmRleC5zZXQoY29udGVudEhhc2gsIGZ1bGxQYXRoKTtcblxuICAgICAgLy8gTG9nIHN1Y2Nlc3NmdWwgc2F2ZVxuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLk1FTU9SWV9TQVZFRCxcbiAgICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgICBzb3VyY2U6ICdNZW1vcnlNYW5hZ2VyLnNhdmUnLFxuICAgICAgICBkZXRhaWxzOiBgU2F2ZWQgbWVtb3J5IHRvICR7cGF0aC5iYXNlbmFtZShmdWxsUGF0aCl9IHdpdGggJHtzdGF0cy50b3RhbEVudHJpZXN9IGVudHJpZXNgXG4gICAgICB9KTtcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX1NBVkVfRkFJTEVELFxuICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICBzb3VyY2U6ICdNZW1vcnlNYW5hZ2VyLnNhdmUnLFxuICAgICAgICBkZXRhaWxzOiBgRmFpbGVkIHRvIHNhdmUgbWVtb3J5IHRvICR7ZmlsZVBhdGh9OiAke2Vycm9yfWBcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gc2F2ZSBtZW1vcnk6ICR7ZXJyb3J9YCk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogTGlzdCBhbGwgYXZhaWxhYmxlIG1lbW9yaWVzXG4gICAqL1xuICBhc3luYyBsaXN0KCk6IFByb21pc2U8TWVtb3J5W10+IHtcbiAgICBjb25zdCBtZW1vcmllczogTWVtb3J5W10gPSBbXTtcblxuICAgIHRyeSB7XG4gICAgICAvLyBHZXQgYWxsIGRhdGUgZm9sZGVyc1xuICAgICAgY29uc3QgZGF0ZUZvbGRlcnMgPSBhd2FpdCB0aGlzLmdldERhdGVGb2xkZXJzKCk7XG5cbiAgICAgIC8vIEFsc28gY2hlY2sgcm9vdCBkaXJlY3RvcnkgZm9yIGFueSBsZWdhY3kgZmlsZXNcbiAgICAgIGNvbnN0IHJvb3RGaWxlcyA9IGF3YWl0IGZzLnJlYWRkaXIodGhpcy5tZW1vcmllc0RpcilcbiAgICAgICAgLnRoZW4oZmlsZXMgPT4gZmlsZXMuZmlsdGVyKGYgPT4gZi5lbmRzV2l0aCgnLnlhbWwnKSkpXG4gICAgICAgIC5jYXRjaCgoKSA9PiBbXSk7XG5cbiAgICAgIC8vIFByb2Nlc3Mgcm9vdCBmaWxlcyBmaXJzdCAobGVnYWN5KVxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHJvb3RGaWxlcykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IG1lbW9yeSA9IGF3YWl0IHRoaXMubG9hZChmaWxlKTtcbiAgICAgICAgICBtZW1vcmllcy5wdXNoKG1lbW9yeSk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgICAgdHlwZTogTUVNT1JZX1NFQ1VSSVRZX0VWRU5UUy5NRU1PUllfTElTVF9JVEVNX0ZBSUxFRCxcbiAgICAgICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgICAgIHNvdXJjZTogJ01lbW9yeU1hbmFnZXIubGlzdCcsXG4gICAgICAgICAgICBkZXRhaWxzOiBgRmFpbGVkIHRvIGxvYWQgJHtmaWxlfTogJHtlcnJvcn1gXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gUHJvY2VzcyBkYXRlIGZvbGRlcnNcbiAgICAgIGZvciAoY29uc3QgZGF0ZUZvbGRlciBvZiBkYXRlRm9sZGVycykge1xuICAgICAgICBjb25zdCBmb2xkZXJQYXRoID0gcGF0aC5qb2luKHRoaXMubWVtb3JpZXNEaXIsIGRhdGVGb2xkZXIpO1xuICAgICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGZzLnJlYWRkaXIoZm9sZGVyUGF0aClcbiAgICAgICAgICAudGhlbihmaWxlcyA9PiBmaWxlcy5maWx0ZXIoZiA9PiBmLmVuZHNXaXRoKCcueWFtbCcpKSlcbiAgICAgICAgICAuY2F0Y2goKCkgPT4gW10pO1xuXG4gICAgICAgIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBtZW1vcnkgPSBhd2FpdCB0aGlzLmxvYWQocGF0aC5qb2luKGRhdGVGb2xkZXIsIGZpbGUpKTtcbiAgICAgICAgICAgIG1lbW9yaWVzLnB1c2gobWVtb3J5KTtcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgICAgICB0eXBlOiBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLk1FTU9SWV9MSVNUX0lURU1fRkFJTEVELFxuICAgICAgICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgICAgICAgIHNvdXJjZTogJ01lbW9yeU1hbmFnZXIubGlzdCcsXG4gICAgICAgICAgICAgIGRldGFpbHM6IGBGYWlsZWQgdG8gbG9hZCAke2RhdGVGb2xkZXJ9LyR7ZmlsZX06ICR7ZXJyb3J9YFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBtZW1vcmllcztcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSA9PT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgLy8gRGlyZWN0b3J5IGRvZXNuJ3QgZXhpc3QgeWV0XG4gICAgICAgIHJldHVybiBbXTtcbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEZpbmQgbWVtb3JpZXMgbWF0Y2hpbmcgYSBwcmVkaWNhdGVcbiAgICovXG4gIGFzeW5jIGZpbmQocHJlZGljYXRlOiAoZWxlbWVudDogTWVtb3J5KSA9PiBib29sZWFuKTogUHJvbWlzZTxNZW1vcnkgfCB1bmRlZmluZWQ+IHtcbiAgICBjb25zdCBtZW1vcmllcyA9IGF3YWl0IHRoaXMubGlzdCgpO1xuICAgIHJldHVybiBtZW1vcmllcy5maW5kKHByZWRpY2F0ZSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBGaW5kIG11bHRpcGxlIG1lbW9yaWVzIG1hdGNoaW5nIGEgcHJlZGljYXRlXG4gICAqL1xuICBhc3luYyBmaW5kTWFueShwcmVkaWNhdGU6IChlbGVtZW50OiBNZW1vcnkpID0+IGJvb2xlYW4pOiBQcm9taXNlPE1lbW9yeVtdPiB7XG4gICAgY29uc3QgbWVtb3JpZXMgPSBhd2FpdCB0aGlzLmxpc3QoKTtcbiAgICByZXR1cm4gbWVtb3JpZXMuZmlsdGVyKHByZWRpY2F0ZSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBEZWxldGUgYSBtZW1vcnkgZmlsZVxuICAgKiBTRUNVUklUWTogVmFsaWRhdGVzIHBhdGggYW5kIGxvZ3MgZGVsZXRpb25cbiAgICovXG4gIGFzeW5jIGRlbGV0ZShmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZ1bGxQYXRoID0gYXdhaXQgdGhpcy52YWxpZGF0ZUFuZFJlc29sdmVQYXRoKGZpbGVQYXRoKTtcbiAgICAgIFxuICAgICAgLy8gQ2hlY2sgaWYgZmlsZSBleGlzdHNcbiAgICAgIGF3YWl0IGZzLmFjY2VzcyhmdWxsUGF0aCk7XG4gICAgICBcbiAgICAgIC8vIERlbGV0ZSB0aGUgZmlsZVxuICAgICAgYXdhaXQgZnMudW5saW5rKGZ1bGxQYXRoKTtcbiAgICAgIFxuICAgICAgLy8gUmVtb3ZlIGZyb20gY2FjaGVcbiAgICAgIHRoaXMubWVtb3J5Q2FjaGUuZGVsZXRlKGZ1bGxQYXRoKTtcbiAgICAgIFxuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiBNRU1PUllfU0VDVVJJVFlfRVZFTlRTLk1FTU9SWV9ERUxFVEVELFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ01lbW9yeU1hbmFnZXIuZGVsZXRlJyxcbiAgICAgICAgZGV0YWlsczogYERlbGV0ZWQgbWVtb3J5IGZpbGU6ICR7cGF0aC5iYXNlbmFtZShmdWxsUGF0aCl9YFxuICAgICAgfSk7XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKChlcnJvciBhcyBhbnkpLmNvZGUgPT09ICdFTk9FTlQnKSB7XG4gICAgICAgIC8vIEZpbGUgZG9lc24ndCBleGlzdCwgbm90IGFuIGVycm9yIGZvciBkZWxldGUgb3BlcmF0aW9uXG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIGEgbWVtb3J5IGZpbGUgZXhpc3RzXG4gICAqL1xuICBhc3luYyBleGlzdHMoZmlsZVBhdGg6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBmdWxsUGF0aCA9IGF3YWl0IHRoaXMudmFsaWRhdGVBbmRSZXNvbHZlUGF0aChmaWxlUGF0aCk7XG4gICAgICBhd2FpdCBmcy5hY2Nlc3MoZnVsbFBhdGgpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IG1lbW9yeSB3aXRoIG1ldGFkYXRhXG4gICAqL1xuICBhc3luYyBjcmVhdGUobWV0YWRhdGE6IFBhcnRpYWw8TWVtb3J5TWV0YWRhdGE+KTogUHJvbWlzZTxNZW1vcnk+IHtcbiAgICByZXR1cm4gbmV3IE1lbW9yeShtZXRhZGF0YSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBJbXBvcnQgYSBtZW1vcnkgZnJvbSBKU09OL1lBTUwgc3RyaW5nXG4gICAqIFNFQ1VSSVRZOiBGdWxsIHZhbGlkYXRpb24gb2YgaW1wb3J0ZWQgY29udGVudFxuICAgKiBAcGFyYW0gZGF0YSBKU09OIG9yIFlBTUwgc3RyaW5nIGNvbnRhaW5pbmcgbWVtb3J5IGRhdGFcbiAgICogQHBhcmFtIGZvcm1hdCBGb3JtYXQgb2YgdGhlIGlucHV0IGRhdGEgKCdqc29uJyBvciAneWFtbCcpXG4gICAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBpbXBvcnRlZCBNZW1vcnkgaW5zdGFuY2VcbiAgICogQHRocm93cyB7RXJyb3J9IFdoZW4gSlNPTi9ZQU1MIHBhcnNpbmcgZmFpbHNcbiAgICogQHRocm93cyB7RXJyb3J9IFdoZW4gaW1wb3J0ZWQgZGF0YSBpcyBtaXNzaW5nIHJlcXVpcmVkIGZpZWxkc1xuICAgKiBAdGhyb3dzIHtFcnJvcn0gV2hlbiBZQU1MIGNvbnRlbnQgZXhjZWVkcyBtYXhpbXVtIGFsbG93ZWQgc2l6ZVxuICAgKiBAdGhyb3dzIHtFcnJvcn0gV2hlbiBpbXBvcnRlZCBtZW1vcnkgZmFpbHMgdmFsaWRhdGlvblxuICAgKi9cbiAgYXN5bmMgaW1wb3J0RWxlbWVudChkYXRhOiBzdHJpbmcsIGZvcm1hdDogJ2pzb24nIHwgJ3lhbWwnID0gJ3lhbWwnKTogUHJvbWlzZTxNZW1vcnk+IHtcbiAgICB0cnkge1xuICAgICAgbGV0IHBhcnNlZDogYW55O1xuICAgICAgXG4gICAgICBpZiAoZm9ybWF0ID09PSAnanNvbicpIHtcbiAgICAgICAgcGFyc2VkID0gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEhJR0ggU0VWRVJJVFkgRklYOiBVc2Ugc2VjdXJlIFlBTUwgcGFyc2luZ1xuICAgICAgICAvLyBNZW1vcnkgaW1wb3J0IGV4cGVjdHMgcHVyZSBZQU1MIChub3QgZnJvbnRtYXR0ZXIpLCBzbyB3ZSBwYXJzZSBpdCBzZWN1cmVseVxuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIEZpcnN0IHZhbGlkYXRlIHRoZSBZQU1MIGNvbnRlbnQgc2l6ZVxuICAgICAgICAgIGlmIChkYXRhLmxlbmd0aCA+IE1FTU9SWV9DT05TVEFOVFMuTUFYX1lBTUxfU0laRSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdZQU1MIGNvbnRlbnQgZXhjZWVkcyBtYXhpbXVtIGFsbG93ZWQgc2l6ZScpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBcbiAgICAgICAgICAvLyBDcmVhdGUgYSB3cmFwcGVyIHRvIHVzZSBTZWN1cmVZYW1sUGFyc2VyIHdpdGggcHVyZSBZQU1MXG4gICAgICAgICAgLy8gQWRkIG1pbmltYWwgZnJvbnRtYXR0ZXIgbWFya2VycyB0byBzYXRpc2Z5IHBhcnNlclxuICAgICAgICAgIGNvbnN0IHdyYXBwZWRZYW1sID0gYC0tLVxcbiR7ZGF0YX1cXG4tLS1cXG5gO1xuICAgICAgICAgIFxuICAgICAgICAgIGNvbnN0IHBhcnNlUmVzdWx0ID0gU2VjdXJlWWFtbFBhcnNlci5wYXJzZSh3cmFwcGVkWWFtbCwge1xuICAgICAgICAgICAgbWF4WWFtbFNpemU6IE1FTU9SWV9DT05TVEFOVFMuTUFYX1lBTUxfU0laRSxcbiAgICAgICAgICAgIHZhbGlkYXRlQ29udGVudDogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIEV4dHJhY3QgdGhlIHBhcnNlZCBkYXRhICh3aWxsIGJlIGluIHRoZSAnZGF0YScgcHJvcGVydHkpXG4gICAgICAgICAgcGFyc2VkID0gcGFyc2VSZXN1bHQuZGF0YTtcbiAgICAgICAgICBcbiAgICAgICAgfSBjYXRjaCAoeWFtbEVycm9yKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIFlBTUw6ICR7eWFtbEVycm9yfWApO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyBWYWxpZGF0ZSBpdCdzIGFuIG9iamVjdFxuICAgICAgICBpZiAoIXBhcnNlZCB8fCB0eXBlb2YgcGFyc2VkICE9PSAnb2JqZWN0Jykge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignWUFNTCBtdXN0IGNvbnRhaW4gYW4gb2JqZWN0Jyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gSGFuZGxlIGRpZmZlcmVudCBzdHJ1Y3R1cmVzIGZyb20gWUFNTCBwYXJzaW5nXG4gICAgICBsZXQgbWV0YWRhdGEgPSBwYXJzZWQubWV0YWRhdGE7XG4gICAgICBsZXQgZW50cmllcyA9IHBhcnNlZC5lbnRyaWVzIHx8IChwYXJzZWQuZGF0YSAmJiBwYXJzZWQuZGF0YS5lbnRyaWVzKTtcbiAgICAgIFxuICAgICAgLy8gVmFsaWRhdGUgcmVxdWlyZWQgZmllbGRzXG4gICAgICBpZiAoIW1ldGFkYXRhIHx8ICFtZXRhZGF0YS5uYW1lKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTWVtb3J5IG11c3QgaGF2ZSBtZXRhZGF0YSB3aXRoIG5hbWUnKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQ3JlYXRlIG1lbW9yeSBpbnN0YW5jZVxuICAgICAgY29uc3QgbWVtb3J5ID0gbmV3IE1lbW9yeShtZXRhZGF0YSk7XG4gICAgICBcbiAgICAgIC8vIExvYWQgZW50cmllcyBpZiBwcmVzZW50XG4gICAgICBpZiAoZW50cmllcykge1xuICAgICAgICBtZW1vcnkuZGVzZXJpYWxpemUoSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgIGlkOiBtZW1vcnkuaWQsXG4gICAgICAgICAgdHlwZTogbWVtb3J5LnR5cGUsXG4gICAgICAgICAgdmVyc2lvbjogbWVtb3J5LnZlcnNpb24sXG4gICAgICAgICAgbWV0YWRhdGE6IG1lbW9yeS5tZXRhZGF0YSxcbiAgICAgICAgICBleHRlbnNpb25zOiBtZW1vcnkuZXh0ZW5zaW9ucyxcbiAgICAgICAgICBlbnRyaWVzOiBlbnRyaWVzXG4gICAgICAgIH0pKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgcmV0dXJuIG1lbW9yeTtcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6IE1FTU9SWV9TRUNVUklUWV9FVkVOVFMuTUVNT1JZX0lNUE9SVF9GQUlMRUQsXG4gICAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgICAgc291cmNlOiAnTWVtb3J5TWFuYWdlci5pbXBvcnRFbGVtZW50JyxcbiAgICAgICAgZGV0YWlsczogYEZhaWxlZCB0byBpbXBvcnQgbWVtb3J5OiAke2Vycm9yfWBcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gaW1wb3J0IG1lbW9yeTogJHtlcnJvcn1gKTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBFeHBvcnQgYSBtZW1vcnkgdG8gWUFNTCBzdHJpbmdcbiAgICovXG4gIGFzeW5jIGV4cG9ydEVsZW1lbnQoZWxlbWVudDogTWVtb3J5KTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCBzdGF0cyA9IGVsZW1lbnQuZ2V0U3RhdHMoKTtcbiAgICBjb25zdCBkYXRhID0ge1xuICAgICAgbWV0YWRhdGE6IGVsZW1lbnQubWV0YWRhdGEsXG4gICAgICBleHRlbnNpb25zOiBlbGVtZW50LmV4dGVuc2lvbnMsXG4gICAgICBzdGF0czoge1xuICAgICAgICB0b3RhbEVudHJpZXM6IHN0YXRzLnRvdGFsRW50cmllcyxcbiAgICAgICAgdG90YWxTaXplOiBzdGF0cy50b3RhbFNpemUsXG4gICAgICAgIG9sZGVzdEVudHJ5OiBzdGF0cy5vbGRlc3RFbnRyeT8udG9JU09TdHJpbmcoKSxcbiAgICAgICAgbmV3ZXN0RW50cnk6IHN0YXRzLm5ld2VzdEVudHJ5Py50b0lTT1N0cmluZygpXG4gICAgICB9LFxuICAgICAgZW50cmllczogSlNPTi5wYXJzZShlbGVtZW50LnNlcmlhbGl6ZSgpKS5lbnRyaWVzXG4gICAgfTtcbiAgICBcbiAgICAvLyBTRUNVUklUWSBGSVg6IFVzZSBzZWN1cmUgWUFNTCBkdW1waW5nXG4gICAgcmV0dXJuIHlhbWwuZHVtcChkYXRhLCB7XG4gICAgICBzY2hlbWE6IHlhbWwuRkFJTFNBRkVfU0NIRU1BLFxuICAgICAgbm9SZWZzOiB0cnVlLFxuICAgICAgc2tpcEludmFsaWQ6IHRydWUsXG4gICAgICBzb3J0S2V5czogdHJ1ZVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogVmFsaWRhdGUgYSBtZW1vcnkgZWxlbWVudFxuICAgKi9cbiAgdmFsaWRhdGUoZWxlbWVudDogTWVtb3J5KTogRWxlbWVudFZhbGlkYXRpb25SZXN1bHQge1xuICAgIHJldHVybiBlbGVtZW50LnZhbGlkYXRlKCk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBhbmQgcmVzb2x2ZSBhIGZpbGUgcGF0aFxuICAgKiBTRUNVUklUWTogUHJldmVudHMgZGlyZWN0b3J5IHRyYXZlcnNhbCBhdHRhY2tzXG4gICAqL1xuICB2YWxpZGF0ZVBhdGgoZmlsZVBhdGg6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBQZXJmb3JtIHN5bmNocm9ub3VzIHZhbGlkYXRpb24gY2hlY2tzXG4gICAgICBjb25zdCBub3JtYWxpemVkID0gcGF0aC5ub3JtYWxpemUoZmlsZVBhdGgpO1xuICAgICAgXG4gICAgICAvLyBDaGVjayBmb3IgcGF0aCB0cmF2ZXJzYWwgYXR0ZW1wdHNcbiAgICAgIGlmIChub3JtYWxpemVkLmluY2x1ZGVzKCcuLicpIHx8IHBhdGguaXNBYnNvbHV0ZShub3JtYWxpemVkKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIEVuc3VyZSBwcm9wZXIgZXh0ZW5zaW9uXG4gICAgICBpZiAoIW5vcm1hbGl6ZWQuZW5kc1dpdGgoJy5tZCcpICYmICFub3JtYWxpemVkLmVuZHNXaXRoKCcueWFtbCcpICYmICFub3JtYWxpemVkLmVuZHNXaXRoKCcueW1sJykpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgXG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgdGhlIGVsZW1lbnQgdHlwZSB0aGlzIG1hbmFnZXIgaGFuZGxlc1xuICAgKi9cbiAgZ2V0RWxlbWVudFR5cGUoKTogRWxlbWVudFR5cGUge1xuICAgIHJldHVybiBFbGVtZW50VHlwZS5NRU1PUlk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgdGhlIGZpbGUgZXh0ZW5zaW9uIGZvciBtZW1vcnkgZmlsZXNcbiAgICovXG4gIGdldEZpbGVFeHRlbnNpb24oKTogc3RyaW5nIHtcbiAgICByZXR1cm4gJy55YW1sJztcbiAgfVxuICBcbiAgLy8gUHJpdmF0ZSBoZWxwZXIgbWV0aG9kc1xuICBcbiAgLyoqXG4gICAqIFZhbGlkYXRlIGFuZCByZXNvbHZlIGEgZmlsZSBwYXRoIHRvIHByZXZlbnQgc2VjdXJpdHkgaXNzdWVzXG4gICAqIEBwYXJhbSBmaWxlUGF0aCBQYXRoIHRvIHZhbGlkYXRlIGFuZCByZXNvbHZlXG4gICAqIEByZXR1cm5zIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSB2YWxpZGF0ZWQgZnVsbCBwYXRoXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBXaGVuIHBhdGggY29udGFpbnMgdHJhdmVyc2FsIGF0dGVtcHRzICguLi8pXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBXaGVuIHBhdGggaXMgYWJzb2x1dGUgb3IgaW52YWxpZFxuICAgKiBAdGhyb3dzIHtFcnJvcn0gV2hlbiBmaWxlIGV4dGVuc2lvbiBpcyBub3QgYWxsb3dlZCAoLm1kLCAueWFtbCwgLnltbClcbiAgICogQHRocm93cyB7RXJyb3J9IFdoZW4gcmVzb2x2ZWQgcGF0aCB3b3VsZCBiZSBvdXRzaWRlIG1lbW9yaWVzIGRpcmVjdG9yeVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2YWxpZGF0ZUFuZFJlc29sdmVQYXRoKGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIC8vIFNFQ1VSSVRZIEZJWDogQ29tcHJlaGVuc2l2ZSBwYXRoIHZhbGlkYXRpb25cbiAgICBjb25zdCBub3JtYWxpemVkID0gcGF0aC5ub3JtYWxpemUoZmlsZVBhdGgpO1xuICAgIFxuICAgIC8vIENoZWNrIGZvciBwYXRoIHRyYXZlcnNhbCBhdHRlbXB0c1xuICAgIGlmIChub3JtYWxpemVkLmluY2x1ZGVzKCcuLicpIHx8IHBhdGguaXNBYnNvbHV0ZShub3JtYWxpemVkKSkge1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnUEFUSF9UUkFWRVJTQUxfQVRURU1QVCcsXG4gICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgIHNvdXJjZTogJ01lbW9yeU1hbmFnZXIudmFsaWRhdGVBbmRSZXNvbHZlUGF0aCcsXG4gICAgICAgIGRldGFpbHM6IGBCbG9ja2VkIHBhdGggdHJhdmVyc2FsIGF0dGVtcHQ6ICR7ZmlsZVBhdGh9YFxuICAgICAgfSk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZmlsZSBwYXRoOiBQYXRoIHRyYXZlcnNhbCBkZXRlY3RlZCcpO1xuICAgIH1cbiAgICBcbiAgICAvLyBFbnN1cmUgcHJvcGVyIGV4dGVuc2lvblxuICAgIGlmICghbm9ybWFsaXplZC5lbmRzV2l0aCgnLm1kJykgJiYgIW5vcm1hbGl6ZWQuZW5kc1dpdGgoJy55YW1sJykgJiYgIW5vcm1hbGl6ZWQuZW5kc1dpdGgoJy55bWwnKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdNZW1vcnkgZmlsZXMgbXVzdCBoYXZlIC5tZCwgLnlhbWwsIG9yIC55bWwgZXh0ZW5zaW9uJyk7XG4gICAgfVxuICAgIFxuICAgIC8vIENvbnN0cnVjdCBmdWxsIHBhdGhcbiAgICBjb25zdCBmdWxsUGF0aCA9IHBhdGguam9pbih0aGlzLm1lbW9yaWVzRGlyLCBub3JtYWxpemVkKTtcbiAgICBcbiAgICAvLyBWZXJpZnkgaXQncyB3aXRoaW4gbWVtb3JpZXMgZGlyZWN0b3J5XG4gICAgY29uc3QgcmVsYXRpdmUgPSBwYXRoLnJlbGF0aXZlKHRoaXMubWVtb3JpZXNEaXIsIGZ1bGxQYXRoKTtcbiAgICBpZiAocmVsYXRpdmUuc3RhcnRzV2l0aCgnLi4nKSB8fCBwYXRoLmlzQWJzb2x1dGUocmVsYXRpdmUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZpbGUgcGF0aCBtdXN0IGJlIHdpdGhpbiBtZW1vcmllcyBkaXJlY3RvcnknKTtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGZ1bGxQYXRoO1xuICB9XG4gIFxuICBwcml2YXRlIHBhcnNlTWVtb3J5RmlsZShwYXJzZWQ6IGFueSk6IHsgbWV0YWRhdGE6IE1lbW9yeU1ldGFkYXRhOyBjb250ZW50OiBzdHJpbmcgfSB7XG4gICAgLy8gRXh0cmFjdCBtZXRhZGF0YSB3aXRoIHZhbGlkYXRpb25cbiAgICBjb25zdCBtZXRhZGF0YTogTWVtb3J5TWV0YWRhdGEgPSB7XG4gICAgICBuYW1lOiBzYW5pdGl6ZUlucHV0KHBhcnNlZC5tZXRhZGF0YT8ubmFtZSB8fCAnVW5uYW1lZCBNZW1vcnknLCAxMDApLFxuICAgICAgZGVzY3JpcHRpb246IHBhcnNlZC5tZXRhZGF0YT8uZGVzY3JpcHRpb24gPyBcbiAgICAgICAgc2FuaXRpemVJbnB1dChwYXJzZWQubWV0YWRhdGEuZGVzY3JpcHRpb24sIDUwMCkgOiBcbiAgICAgICAgJycsXG4gICAgICB2ZXJzaW9uOiBwYXJzZWQubWV0YWRhdGE/LnZlcnNpb24gfHwgJzEuMC4wJyxcbiAgICAgIGF1dGhvcjogcGFyc2VkLm1ldGFkYXRhPy5hdXRob3IsXG4gICAgICBjcmVhdGVkOiBwYXJzZWQubWV0YWRhdGE/LmNyZWF0ZWQsXG4gICAgICBtb2RpZmllZDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgdGFnczogQXJyYXkuaXNBcnJheShwYXJzZWQubWV0YWRhdGE/LnRhZ3MpID9cbiAgICAgICAgcGFyc2VkLm1ldGFkYXRhLnRhZ3MubWFwKCh0YWc6IHN0cmluZykgPT4gc2FuaXRpemVJbnB1dCh0YWcsIE1FTU9SWV9DT05TVEFOVFMuTUFYX1RBR19MRU5HVEgpKSA6XG4gICAgICAgIFtdLFxuICAgICAgc3RvcmFnZUJhY2tlbmQ6IHBhcnNlZC5tZXRhZGF0YT8uc3RvcmFnZUJhY2tlbmQgfHwgTUVNT1JZX0NPTlNUQU5UUy5ERUZBVUxUX1NUT1JBR0VfQkFDS0VORCxcbiAgICAgIHJldGVudGlvbkRheXM6IHBhcnNlZC5tZXRhZGF0YT8ucmV0ZW50aW9uRGF5cyB8fCBNRU1PUllfQ09OU1RBTlRTLkRFRkFVTFRfUkVURU5USU9OX0RBWVMsXG4gICAgICBwcml2YWN5TGV2ZWw6IHBhcnNlZC5tZXRhZGF0YT8ucHJpdmFjeUxldmVsIHx8IE1FTU9SWV9DT05TVEFOVFMuREVGQVVMVF9QUklWQUNZX0xFVkVMLFxuICAgICAgc2VhcmNoYWJsZTogcGFyc2VkLm1ldGFkYXRhPy5zZWFyY2hhYmxlICE9PSBmYWxzZSxcbiAgICAgIG1heEVudHJpZXM6IHBhcnNlZC5tZXRhZGF0YT8ubWF4RW50cmllcyB8fCBNRU1PUllfQ09OU1RBTlRTLk1BWF9FTlRSSUVTX0RFRkFVTFRcbiAgICB9O1xuICAgIFxuICAgIC8vIEV4dHJhY3QgY29udGVudCAoaWYgYW55KVxuICAgIGNvbnN0IGNvbnRlbnQgPSBwYXJzZWQuY29udGVudCB8fCAnJztcbiAgICBcbiAgICByZXR1cm4geyBtZXRhZGF0YSwgY29udGVudCB9O1xuICB9XG59Il19
|