@dollhousemcp/mcp-server 1.6.10 → 1.7.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 +8 -0
- package/README.github.md +1036 -0
- package/README.md +165 -1668
- package/README.md.backup +298 -0
- package/README.npm.md +298 -0
- package/dist/cache/CollectionIndexCache.d.ts.map +1 -1
- package/dist/cache/CollectionIndexCache.js +2 -2
- package/dist/collection/CollectionBrowser.d.ts.map +1 -1
- package/dist/collection/CollectionBrowser.js +6 -3
- package/dist/collection/CollectionIndexManager.d.ts.map +1 -1
- package/dist/collection/CollectionIndexManager.js +4 -2
- package/dist/collection/GitHubClient.d.ts.map +1 -1
- package/dist/collection/GitHubClient.js +5 -1
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.d.ts.map +1 -1
- package/dist/generated/version.js +3 -3
- package/dist/index.d.ts +0 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -98
- package/dist/persona/export-import/index.d.ts +1 -3
- package/dist/persona/export-import/index.d.ts.map +1 -1
- package/dist/persona/export-import/index.js +2 -3
- package/dist/portfolio/PortfolioRepoManager.d.ts +1 -1
- package/dist/portfolio/PortfolioRepoManager.d.ts.map +1 -1
- package/dist/portfolio/PortfolioRepoManager.js +118 -33
- package/dist/security/contentValidator.js +8 -8
- package/dist/server/tools/PersonaTools.d.ts.map +1 -1
- package/dist/server/tools/PersonaTools.js +36 -76
- package/dist/server/types.d.ts +0 -2
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +1 -1
- package/dist/tools/portfolio/PortfolioElementAdapter.d.ts.map +1 -1
- package/dist/tools/portfolio/PortfolioElementAdapter.js +3 -1
- package/package.json +9 -3
- package/dist/persona/export-import/PersonaSharer.d.ts +0 -81
- package/dist/persona/export-import/PersonaSharer.d.ts.map +0 -1
- package/dist/persona/export-import/PersonaSharer.js +0 -678
|
@@ -1,678 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persona sharing functionality via URLs
|
|
3
|
-
*
|
|
4
|
-
* SECURITY FIX IMPLEMENTED (Defense-in-Depth Validation):
|
|
5
|
-
* 1. CRITICAL: Added validate-before-return pattern in all import methods
|
|
6
|
-
* 2. HIGH: Content security validation using ContentValidator before data return
|
|
7
|
-
* 3. MEDIUM: Size validation to prevent memory exhaustion attacks
|
|
8
|
-
* 4. MEDIUM: Structure validation to prevent malformed data processing
|
|
9
|
-
* 5. DEFENSE-IN-DEPTH: Multiple validation layers before PersonaImporter processing
|
|
10
|
-
*
|
|
11
|
-
* This provides defense-in-depth security by validating content at the earliest
|
|
12
|
-
* possible point before any data is returned to calling code or file operations.
|
|
13
|
-
*/
|
|
14
|
-
import { PersonaExporter } from './PersonaExporter.js';
|
|
15
|
-
import { TokenManager } from '../../security/tokenManager.js';
|
|
16
|
-
import { SecurityError } from '../../security/errors.js';
|
|
17
|
-
import { logger } from '../../utils/logger.js';
|
|
18
|
-
import { SecurityMonitor } from '../../security/securityMonitor.js';
|
|
19
|
-
import { ErrorHandler, ErrorCategory } from '../../utils/ErrorHandler.js';
|
|
20
|
-
import { ValidationErrorCodes, NetworkErrorCodes } from '../../utils/errorCodes.js';
|
|
21
|
-
import { RateLimiter } from '../../utils/RateLimiter.js';
|
|
22
|
-
import { ContentValidator } from '../../security/contentValidator.js';
|
|
23
|
-
import { validateContentSize } from '../../security/InputValidator.js';
|
|
24
|
-
export class PersonaSharer {
|
|
25
|
-
githubClient;
|
|
26
|
-
currentUser;
|
|
27
|
-
exporter;
|
|
28
|
-
githubRateLimiter;
|
|
29
|
-
constructor(githubClient, currentUser) {
|
|
30
|
-
this.githubClient = githubClient;
|
|
31
|
-
this.currentUser = currentUser;
|
|
32
|
-
this.exporter = new PersonaExporter(currentUser);
|
|
33
|
-
// GitHub API rate limit: 60 requests per hour for unauthenticated
|
|
34
|
-
// 5000 per hour for authenticated - use TokenManager to check
|
|
35
|
-
const hasValidToken = TokenManager.getGitHubToken() !== null;
|
|
36
|
-
this.githubRateLimiter = new RateLimiter({
|
|
37
|
-
maxRequests: hasValidToken ? 100 : 30, // Conservative limits
|
|
38
|
-
windowMs: 60 * 60 * 1000, // 1 hour
|
|
39
|
-
minDelayMs: 1000 // Minimum 1 second between requests
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Share a persona via GitHub Gist
|
|
44
|
-
*/
|
|
45
|
-
async sharePersona(persona, expiryDays = 7) {
|
|
46
|
-
try {
|
|
47
|
-
// Validate gist permissions if token is available
|
|
48
|
-
const token = TokenManager.getGitHubToken();
|
|
49
|
-
let hasValidToken = false;
|
|
50
|
-
if (token) {
|
|
51
|
-
try {
|
|
52
|
-
const validation = await TokenManager.ensureTokenPermissions('gist');
|
|
53
|
-
if (!validation.isValid) {
|
|
54
|
-
const safeMessage = TokenManager.createSafeErrorMessage(validation.error || 'Unknown validation error', token);
|
|
55
|
-
logger.warn('GitHub token lacks gist permissions, falling back to base64 URL', { error: safeMessage });
|
|
56
|
-
// Continue to fallback instead of failing
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
hasValidToken = true;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
// Handle rate limiting or other security errors gracefully
|
|
64
|
-
if (error instanceof SecurityError && error.code === 'RATE_LIMIT_EXCEEDED') {
|
|
65
|
-
logger.warn('Token validation rate limited, falling back to base64 URL', {
|
|
66
|
-
error: 'Rate limit exceeded for token validation'
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
else if (error instanceof Error) {
|
|
70
|
-
const safeMessage = TokenManager.createSafeErrorMessage(error.message, token);
|
|
71
|
-
logger.warn('Token validation failed, falling back to base64 URL', { error: safeMessage });
|
|
72
|
-
}
|
|
73
|
-
// Continue to fallback instead of failing
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// Export persona to structured format
|
|
77
|
-
const exportData = this.exporter.exportPersona(persona);
|
|
78
|
-
// Add sharing metadata
|
|
79
|
-
const shareData = {
|
|
80
|
-
...exportData,
|
|
81
|
-
sharedAt: new Date().toISOString(),
|
|
82
|
-
sharedBy: this.currentUser || 'anonymous',
|
|
83
|
-
expiresAt: new Date(Date.now() + expiryDays * 24 * 60 * 60 * 1000).toISOString(),
|
|
84
|
-
shareVersion: '1.0.0'
|
|
85
|
-
};
|
|
86
|
-
// Create GitHub Gist if token has proper permissions
|
|
87
|
-
if (hasValidToken) {
|
|
88
|
-
const gistResult = await this.createGist(persona.metadata.name, shareData);
|
|
89
|
-
if (gistResult.success) {
|
|
90
|
-
return {
|
|
91
|
-
success: true,
|
|
92
|
-
url: gistResult.url,
|
|
93
|
-
gistId: gistResult.gistId,
|
|
94
|
-
expiresAt: shareData.expiresAt,
|
|
95
|
-
message: this.formatShareSuccess(gistResult.url, shareData.expiresAt)
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
// Fallback to base64 URL if Gist fails or no token
|
|
100
|
-
return this.createBase64Url(shareData);
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
104
|
-
const safeMessage = TokenManager.createSafeErrorMessage(errorMessage);
|
|
105
|
-
logger.error('Share error', { error: safeMessage });
|
|
106
|
-
return {
|
|
107
|
-
success: false,
|
|
108
|
-
message: `Failed to share persona: ${safeMessage}`
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Import a persona from a share URL
|
|
114
|
-
* SECURITY FIX: Validate ALL content before returning any data
|
|
115
|
-
*/
|
|
116
|
-
async importFromUrl(url) {
|
|
117
|
-
try {
|
|
118
|
-
// Validate URL first
|
|
119
|
-
if (!this.validateShareUrl(url)) {
|
|
120
|
-
return {
|
|
121
|
-
success: false,
|
|
122
|
-
message: 'Invalid or unsafe URL provided'
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
// Check if it's a GitHub Gist URL
|
|
126
|
-
const gistId = this.extractGistId(url);
|
|
127
|
-
if (gistId) {
|
|
128
|
-
return await this.importFromGist(gistId);
|
|
129
|
-
}
|
|
130
|
-
// Check if it's a base64 URL
|
|
131
|
-
if (url.includes('#dollhouse-persona=')) {
|
|
132
|
-
return await this.importFromBase64Url(url);
|
|
133
|
-
}
|
|
134
|
-
// Validate URL for security
|
|
135
|
-
if (!this.validateShareUrl(url)) {
|
|
136
|
-
throw ErrorHandler.createError('Invalid or potentially malicious URL', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
|
|
137
|
-
}
|
|
138
|
-
// Try direct fetch with timeout
|
|
139
|
-
const controller = new AbortController();
|
|
140
|
-
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
|
|
141
|
-
try {
|
|
142
|
-
const response = await fetch(url, {
|
|
143
|
-
signal: controller.signal,
|
|
144
|
-
headers: {
|
|
145
|
-
'User-Agent': 'DollhouseMCP/1.0',
|
|
146
|
-
'Accept': 'application/json'
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
clearTimeout(timeoutId);
|
|
150
|
-
if (!response.ok) {
|
|
151
|
-
throw ErrorHandler.createError(`Request failed with status ${response.status}`, ErrorCategory.NETWORK_ERROR, NetworkErrorCodes.REQUEST_FAILED);
|
|
152
|
-
}
|
|
153
|
-
// ENHANCED SECURITY FIX: Comprehensive Content-Type validation
|
|
154
|
-
const contentType = response.headers.get('content-type');
|
|
155
|
-
const contentTypeValidation = this.validateContentType(contentType, 'application/json');
|
|
156
|
-
if (!contentTypeValidation.isValid) {
|
|
157
|
-
throw ErrorHandler.createError(`Invalid response type: ${contentTypeValidation.error}`, ErrorCategory.NETWORK_ERROR, NetworkErrorCodes.INVALID_RESPONSE);
|
|
158
|
-
}
|
|
159
|
-
// Check response size to prevent memory exhaustion
|
|
160
|
-
const contentLength = response.headers.get('content-length');
|
|
161
|
-
const maxSize = 5 * 1024 * 1024; // 5MB max
|
|
162
|
-
if (contentLength && parseInt(contentLength) > maxSize) {
|
|
163
|
-
throw ErrorHandler.createError('Response too large', ErrorCategory.VALIDATION_ERROR, NetworkErrorCodes.RESPONSE_TOO_LARGE);
|
|
164
|
-
}
|
|
165
|
-
const data = await response.json();
|
|
166
|
-
// SECURITY FIX: Validate content before returning
|
|
167
|
-
const dataValidation = await this.validatePersonaData(data);
|
|
168
|
-
if (!dataValidation.isValid) {
|
|
169
|
-
throw new SecurityError(`Content validation failed: ${dataValidation.error}`);
|
|
170
|
-
}
|
|
171
|
-
return {
|
|
172
|
-
success: true,
|
|
173
|
-
data,
|
|
174
|
-
message: 'Successfully retrieved persona data'
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
finally {
|
|
178
|
-
clearTimeout(timeoutId);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
catch (error) {
|
|
182
|
-
logger.error('Import from URL error', error);
|
|
183
|
-
return {
|
|
184
|
-
success: false,
|
|
185
|
-
message: `Failed to import from URL: ${error instanceof Error ? error.message : String(error)}`
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Create a GitHub Gist
|
|
191
|
-
*/
|
|
192
|
-
async createGist(personaName, data) {
|
|
193
|
-
try {
|
|
194
|
-
// Use TokenManager for secure token handling
|
|
195
|
-
const token = TokenManager.getGitHubToken();
|
|
196
|
-
if (!token) {
|
|
197
|
-
logger.info('No valid GitHub token available for Gist creation');
|
|
198
|
-
return { success: false };
|
|
199
|
-
}
|
|
200
|
-
// Check rate limit
|
|
201
|
-
const rateLimitStatus = this.githubRateLimiter.checkLimit();
|
|
202
|
-
if (!rateLimitStatus.allowed) {
|
|
203
|
-
logger.warn(`GitHub API rate limit exceeded. Retry after ${rateLimitStatus.retryAfterMs}ms`);
|
|
204
|
-
return { success: false };
|
|
205
|
-
}
|
|
206
|
-
const controller = new AbortController();
|
|
207
|
-
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
|
|
208
|
-
try {
|
|
209
|
-
const response = await fetch('https://api.github.com/gists', {
|
|
210
|
-
method: 'POST',
|
|
211
|
-
signal: controller.signal,
|
|
212
|
-
headers: {
|
|
213
|
-
'Authorization': `Bearer ${token}`,
|
|
214
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
215
|
-
'Content-Type': 'application/json',
|
|
216
|
-
'User-Agent': 'DollhouseMCP/1.0'
|
|
217
|
-
},
|
|
218
|
-
body: JSON.stringify({
|
|
219
|
-
description: `DollhouseMCP Persona: ${personaName}`,
|
|
220
|
-
public: false, // Private gist for security
|
|
221
|
-
files: {
|
|
222
|
-
'persona.json': {
|
|
223
|
-
content: JSON.stringify(data, null, 2)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
});
|
|
228
|
-
clearTimeout(timeoutId);
|
|
229
|
-
if (!response.ok) {
|
|
230
|
-
throw ErrorHandler.createError(`GitHub API error: ${response.status}`, ErrorCategory.NETWORK_ERROR, NetworkErrorCodes.API_ERROR);
|
|
231
|
-
}
|
|
232
|
-
// ENHANCED SECURITY FIX: Comprehensive Content-Type validation for GitHub API
|
|
233
|
-
const contentType = response.headers.get('content-type');
|
|
234
|
-
const gistContentTypeValidation = this.validateContentType(contentType, 'application/json');
|
|
235
|
-
if (!gistContentTypeValidation.isValid) {
|
|
236
|
-
throw ErrorHandler.createError(`Invalid GitHub API response type: ${gistContentTypeValidation.error}`, ErrorCategory.NETWORK_ERROR, NetworkErrorCodes.INVALID_RESPONSE);
|
|
237
|
-
}
|
|
238
|
-
const gist = await response.json();
|
|
239
|
-
// Consume the rate limit token after successful request
|
|
240
|
-
this.githubRateLimiter.consumeToken();
|
|
241
|
-
return {
|
|
242
|
-
success: true,
|
|
243
|
-
url: gist.html_url,
|
|
244
|
-
gistId: gist.id
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
finally {
|
|
248
|
-
clearTimeout(timeoutId);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
253
|
-
const safeMessage = TokenManager.createSafeErrorMessage(errorMessage);
|
|
254
|
-
logger.error('Gist creation error', { error: safeMessage });
|
|
255
|
-
return { success: false };
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Create a base64 URL (fallback)
|
|
260
|
-
*/
|
|
261
|
-
createBase64Url(data) {
|
|
262
|
-
const base64 = this.exporter.toBase64(data);
|
|
263
|
-
const url = `https://dollhousemcp.com/import#dollhouse-persona=${base64}`;
|
|
264
|
-
return {
|
|
265
|
-
success: true,
|
|
266
|
-
url,
|
|
267
|
-
expiresAt: data.expiresAt,
|
|
268
|
-
message: this.formatShareSuccess(url, data.expiresAt)
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Import from GitHub Gist
|
|
273
|
-
*/
|
|
274
|
-
async importFromGist(gistId) {
|
|
275
|
-
try {
|
|
276
|
-
// Check rate limit
|
|
277
|
-
const rateLimitStatus = this.githubRateLimiter.checkLimit();
|
|
278
|
-
if (!rateLimitStatus.allowed) {
|
|
279
|
-
throw ErrorHandler.createError(`GitHub API rate limit exceeded. Please try again in ${Math.ceil(rateLimitStatus.retryAfterMs / 1000)} seconds`, ErrorCategory.NETWORK_ERROR, NetworkErrorCodes.RATE_LIMIT_EXCEEDED);
|
|
280
|
-
}
|
|
281
|
-
const gistUrl = `https://api.github.com/gists/${gistId}`;
|
|
282
|
-
// Validate URL (should always pass for GitHub API)
|
|
283
|
-
if (!this.validateShareUrl(gistUrl)) {
|
|
284
|
-
throw ErrorHandler.createError('Invalid GitHub API URL', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
|
|
285
|
-
}
|
|
286
|
-
const controller = new AbortController();
|
|
287
|
-
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout for API
|
|
288
|
-
try {
|
|
289
|
-
const response = await fetch(gistUrl, {
|
|
290
|
-
signal: controller.signal,
|
|
291
|
-
headers: {
|
|
292
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
293
|
-
'User-Agent': 'DollhouseMCP/1.0'
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
clearTimeout(timeoutId);
|
|
297
|
-
if (!response.ok) {
|
|
298
|
-
throw ErrorHandler.createError(`Failed to fetch gist: ${response.status}`, ErrorCategory.NETWORK_ERROR, NetworkErrorCodes.FETCH_FAILED);
|
|
299
|
-
}
|
|
300
|
-
// ENHANCED SECURITY FIX: Comprehensive Content-Type validation for GitHub API
|
|
301
|
-
const contentType = response.headers.get('content-type');
|
|
302
|
-
const gistContentTypeValidation = this.validateContentType(contentType, 'application/json');
|
|
303
|
-
if (!gistContentTypeValidation.isValid) {
|
|
304
|
-
throw ErrorHandler.createError(`Invalid GitHub API response type: ${gistContentTypeValidation.error}`, ErrorCategory.NETWORK_ERROR, NetworkErrorCodes.INVALID_RESPONSE);
|
|
305
|
-
}
|
|
306
|
-
const gist = await response.json();
|
|
307
|
-
const personaFile = gist.files['persona.json'];
|
|
308
|
-
if (!personaFile) {
|
|
309
|
-
throw ErrorHandler.createError('No persona data found in gist', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_INPUT);
|
|
310
|
-
}
|
|
311
|
-
const data = JSON.parse(personaFile.content);
|
|
312
|
-
// Check expiry
|
|
313
|
-
if (data.expiresAt && new Date(data.expiresAt) < new Date()) {
|
|
314
|
-
return {
|
|
315
|
-
success: false,
|
|
316
|
-
message: 'This share link has expired'
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
// SECURITY FIX: Validate content before returning
|
|
320
|
-
const gistDataValidation = await this.validatePersonaData(data);
|
|
321
|
-
if (!gistDataValidation.isValid) {
|
|
322
|
-
throw new SecurityError(`Content validation failed: ${gistDataValidation.error}`);
|
|
323
|
-
}
|
|
324
|
-
// Consume the rate limit token after successful request
|
|
325
|
-
this.githubRateLimiter.consumeToken();
|
|
326
|
-
return {
|
|
327
|
-
success: true,
|
|
328
|
-
data,
|
|
329
|
-
message: 'Successfully retrieved persona from GitHub'
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
finally {
|
|
333
|
-
clearTimeout(timeoutId);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
catch (error) {
|
|
337
|
-
logger.error('Gist import error', error);
|
|
338
|
-
return {
|
|
339
|
-
success: false,
|
|
340
|
-
message: `Failed to import from gist: ${error instanceof Error ? error.message : String(error)}`
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Validate URL for security (prevent SSRF attacks)
|
|
346
|
-
*/
|
|
347
|
-
validateShareUrl(url) {
|
|
348
|
-
try {
|
|
349
|
-
// 1. URL length check to prevent DoS
|
|
350
|
-
if (url.length > 2048) {
|
|
351
|
-
logger.warn('URL exceeds maximum length', { urlLength: url.length });
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
const parsed = new URL(url);
|
|
355
|
-
// 2. Protocol check - only allow http/https
|
|
356
|
-
if (!['https:', 'http:'].includes(parsed.protocol)) {
|
|
357
|
-
logger.warn('Invalid protocol in URL', { protocol: parsed.protocol });
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
// 3. Port restrictions - block non-standard ports that could be internal services
|
|
361
|
-
const port = parsed.port || (parsed.protocol === 'https:' ? '443' : '80');
|
|
362
|
-
const allowedPorts = ['80', '443', '8080', '8443'];
|
|
363
|
-
if (!allowedPorts.includes(port)) {
|
|
364
|
-
logger.warn('Blocked non-standard port', { port });
|
|
365
|
-
return false;
|
|
366
|
-
}
|
|
367
|
-
// 4. Hostname validation
|
|
368
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
369
|
-
// Block various localhost representations
|
|
370
|
-
const blockedHostnames = [
|
|
371
|
-
'localhost',
|
|
372
|
-
'localhost.localdomain',
|
|
373
|
-
'0.0.0.0',
|
|
374
|
-
'0',
|
|
375
|
-
'0x0',
|
|
376
|
-
'0x00000000',
|
|
377
|
-
'[::1]',
|
|
378
|
-
'[::ffff:127.0.0.1]',
|
|
379
|
-
'[0000:0000:0000:0000:0000:0000:0000:0001]'
|
|
380
|
-
];
|
|
381
|
-
if (blockedHostnames.includes(hostname)) {
|
|
382
|
-
logger.warn('Blocked localhost hostname', { hostname });
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
// Block private IP ranges with comprehensive patterns
|
|
386
|
-
const privateIpPatterns = [
|
|
387
|
-
/^127\./, // Loopback
|
|
388
|
-
/^10\./, // Private class A
|
|
389
|
-
/^192\.168\./, // Private class C
|
|
390
|
-
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // Private class B
|
|
391
|
-
/^169\.254\./, // Link-local
|
|
392
|
-
/^fc00:/i, // IPv6 private
|
|
393
|
-
/^fe80:/i, // IPv6 link-local
|
|
394
|
-
/^::1$/, // IPv6 loopback
|
|
395
|
-
/^::ffff:0?:?0?:?0?:?0?$/i, // IPv6 mapped IPv4
|
|
396
|
-
/^100\.6[4-9]\./, // Carrier-grade NAT
|
|
397
|
-
/^100\.[7-9][0-9]\./, // Carrier-grade NAT
|
|
398
|
-
/^100\.1[0-2][0-9]\./, // Carrier-grade NAT
|
|
399
|
-
/^0\./, // Reserved
|
|
400
|
-
/^255\.255\.255\.255$/ // Broadcast
|
|
401
|
-
];
|
|
402
|
-
if (privateIpPatterns.some(pattern => pattern.test(hostname))) {
|
|
403
|
-
logger.warn('Blocked private IP range', { hostname });
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
// Block cloud metadata endpoints
|
|
407
|
-
const metadataEndpoints = [
|
|
408
|
-
'169.254.169.254', // AWS/GCP/Azure
|
|
409
|
-
'metadata.google.internal',
|
|
410
|
-
'metadata.azure.com',
|
|
411
|
-
'100.100.100.200' // Alibaba Cloud
|
|
412
|
-
];
|
|
413
|
-
if (metadataEndpoints.includes(hostname)) {
|
|
414
|
-
logger.warn('Blocked cloud metadata endpoint', { hostname });
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
// Block numeric IP representations that could bypass checks
|
|
418
|
-
if (/^\d+$/.test(hostname)) {
|
|
419
|
-
// Convert decimal to IP and check
|
|
420
|
-
const num = parseInt(hostname, 10);
|
|
421
|
-
if (num <= 0xFFFFFFFF) {
|
|
422
|
-
const ip = `${(num >>> 24) & 0xFF}.${(num >>> 16) & 0xFF}.${(num >>> 8) & 0xFF}.${num & 0xFF}`;
|
|
423
|
-
logger.warn('Blocked numeric IP representation', { hostname, resolvedIp: ip });
|
|
424
|
-
return false;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
// Block hex IP representations
|
|
428
|
-
if (/^0x[0-9a-f]+$/i.test(hostname)) {
|
|
429
|
-
logger.warn('Blocked hex IP representation', { hostname });
|
|
430
|
-
return false;
|
|
431
|
-
}
|
|
432
|
-
// Validate domain format (basic check)
|
|
433
|
-
// Allow GitHub domains and common share platforms
|
|
434
|
-
const trustedDomains = [
|
|
435
|
-
'github.com',
|
|
436
|
-
'gist.github.com',
|
|
437
|
-
'api.github.com',
|
|
438
|
-
'raw.githubusercontent.com',
|
|
439
|
-
'dollhousemcp.com'
|
|
440
|
-
];
|
|
441
|
-
// Check if it's a trusted domain
|
|
442
|
-
const isTrustedDomain = trustedDomains.some(domain => hostname === domain || hostname.endsWith(`.${domain}`));
|
|
443
|
-
if (!isTrustedDomain) {
|
|
444
|
-
// For non-trusted domains, apply stricter validation
|
|
445
|
-
// Must be a valid domain format, not just an IP
|
|
446
|
-
const domainPattern = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i;
|
|
447
|
-
const ipv4Pattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
448
|
-
const ipv6Pattern = /^\[?([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}\]?$/i;
|
|
449
|
-
if (ipv4Pattern.test(hostname) || ipv6Pattern.test(hostname)) {
|
|
450
|
-
logger.warn('Direct IP access not allowed for untrusted sources', { hostname });
|
|
451
|
-
return false;
|
|
452
|
-
}
|
|
453
|
-
if (!domainPattern.test(hostname)) {
|
|
454
|
-
logger.warn('Invalid domain format', { hostname });
|
|
455
|
-
return false;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
logger.debug('URL validation passed', {
|
|
459
|
-
hostname,
|
|
460
|
-
protocol: parsed.protocol,
|
|
461
|
-
isTrusted: isTrustedDomain
|
|
462
|
-
});
|
|
463
|
-
return true;
|
|
464
|
-
}
|
|
465
|
-
catch (error) {
|
|
466
|
-
logger.warn('URL validation error', { error: error instanceof Error ? error.message : 'Unknown error' });
|
|
467
|
-
return false;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Import from base64 URL
|
|
472
|
-
*/
|
|
473
|
-
async importFromBase64Url(url) {
|
|
474
|
-
try {
|
|
475
|
-
// Limit base64 length to prevent ReDoS attacks (10KB max for base64 encoded data)
|
|
476
|
-
const match = url.match(/#dollhouse-persona=([A-Za-z0-9+/=]{1,10000})$/);
|
|
477
|
-
if (!match) {
|
|
478
|
-
throw ErrorHandler.createError('Invalid share URL format', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_FORMAT);
|
|
479
|
-
}
|
|
480
|
-
const base64 = match[1];
|
|
481
|
-
const json = Buffer.from(base64, 'base64').toString('utf-8');
|
|
482
|
-
const data = JSON.parse(json);
|
|
483
|
-
// Check expiry
|
|
484
|
-
if (data.expiresAt && new Date(data.expiresAt) < new Date()) {
|
|
485
|
-
return {
|
|
486
|
-
success: false,
|
|
487
|
-
message: 'This share link has expired'
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
// SECURITY FIX: Validate content before returning
|
|
491
|
-
const base64DataValidation = await this.validatePersonaData(data);
|
|
492
|
-
if (!base64DataValidation.isValid) {
|
|
493
|
-
throw new SecurityError(`Content validation failed: ${base64DataValidation.error}`);
|
|
494
|
-
}
|
|
495
|
-
return {
|
|
496
|
-
success: true,
|
|
497
|
-
data,
|
|
498
|
-
message: 'Successfully decoded persona data'
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
catch (error) {
|
|
502
|
-
return {
|
|
503
|
-
success: false,
|
|
504
|
-
message: `Failed to decode share URL: ${error instanceof Error ? error.message : String(error)}`
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* Extract Gist ID from GitHub URL
|
|
510
|
-
*/
|
|
511
|
-
extractGistId(url) {
|
|
512
|
-
const match = url.match(/gist\.github\.com\/[^\/]+\/([a-f0-9]+)/);
|
|
513
|
-
return match ? match[1] : null;
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Format share success message
|
|
517
|
-
*/
|
|
518
|
-
formatShareSuccess(url, expiresAt) {
|
|
519
|
-
const expiryDate = new Date(expiresAt);
|
|
520
|
-
const daysUntilExpiry = Math.ceil((expiryDate.getTime() - Date.now()) / (24 * 60 * 60 * 1000));
|
|
521
|
-
return `✅ Successfully created share link!
|
|
522
|
-
|
|
523
|
-
🔗 Share URL:
|
|
524
|
-
${url}
|
|
525
|
-
|
|
526
|
-
⏱️ Expires: ${expiryDate.toLocaleDateString()} (${daysUntilExpiry} days)
|
|
527
|
-
|
|
528
|
-
📋 To share this persona:
|
|
529
|
-
1. Copy the URL above
|
|
530
|
-
2. Share it with others
|
|
531
|
-
3. They can import using: import_from_url "${url}"
|
|
532
|
-
|
|
533
|
-
🔒 Privacy: This link is private and will expire automatically.`;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* SECURITY FIX: Validate persona data before any processing
|
|
537
|
-
* This provides defense-in-depth validation before content reaches file operations
|
|
538
|
-
*/
|
|
539
|
-
async validatePersonaData(data) {
|
|
540
|
-
try {
|
|
541
|
-
// Basic structure validation
|
|
542
|
-
if (!data || typeof data !== 'object') {
|
|
543
|
-
return { isValid: false, error: 'Invalid data structure' };
|
|
544
|
-
}
|
|
545
|
-
// Validate required fields for persona data
|
|
546
|
-
if (data.metadata && (!data.metadata.name || !data.metadata.description)) {
|
|
547
|
-
return { isValid: false, error: 'Missing required persona metadata' };
|
|
548
|
-
}
|
|
549
|
-
// Validate content if present
|
|
550
|
-
if (data.content) {
|
|
551
|
-
// Size validation
|
|
552
|
-
try {
|
|
553
|
-
validateContentSize(data.content, 100 * 1024); // 100KB limit
|
|
554
|
-
}
|
|
555
|
-
catch (error) {
|
|
556
|
-
return { isValid: false, error: `Content size validation failed: ${error instanceof Error ? error.message : 'Unknown error'}` };
|
|
557
|
-
}
|
|
558
|
-
// Content security validation
|
|
559
|
-
const contentValidation = ContentValidator.validateAndSanitize(data.content);
|
|
560
|
-
if (!contentValidation.isValid && contentValidation.severity === 'critical') {
|
|
561
|
-
return {
|
|
562
|
-
isValid: false,
|
|
563
|
-
error: `Critical security threat detected: ${contentValidation.detectedPatterns?.join(', ')}`
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
// Validate bundle structure if it's a bundle
|
|
568
|
-
if (data.personas && Array.isArray(data.personas)) {
|
|
569
|
-
for (const persona of data.personas) {
|
|
570
|
-
const personaValidation = await this.validatePersonaData(persona);
|
|
571
|
-
if (!personaValidation.isValid) {
|
|
572
|
-
return { isValid: false, error: `Bundle validation failed: ${personaValidation.error}` };
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return { isValid: true };
|
|
577
|
-
}
|
|
578
|
-
catch (error) {
|
|
579
|
-
return {
|
|
580
|
-
isValid: false,
|
|
581
|
-
error: `Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* ENHANCED SECURITY FIX: Comprehensive Content-Type validation
|
|
587
|
-
* Strengthens MIME type validation with comprehensive security checks
|
|
588
|
-
*/
|
|
589
|
-
validateContentType(contentType, expectedType) {
|
|
590
|
-
// Check if Content-Type header exists
|
|
591
|
-
if (!contentType) {
|
|
592
|
-
return {
|
|
593
|
-
isValid: false,
|
|
594
|
-
error: 'Missing Content-Type header'
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
// Normalize and sanitize the content type
|
|
598
|
-
const normalizedContentType = contentType.toLowerCase().trim();
|
|
599
|
-
const normalizedExpectedType = expectedType.toLowerCase().trim();
|
|
600
|
-
// Validate Content-Type format (basic MIME type structure)
|
|
601
|
-
const mimeTypePattern = /^[a-z0-9][a-z0-9!#$&\-\^_]*\/[a-z0-9][a-z0-9!#$&\-\^_]*(?:\s*;.*)?$/;
|
|
602
|
-
if (!mimeTypePattern.test(normalizedContentType)) {
|
|
603
|
-
return {
|
|
604
|
-
isValid: false,
|
|
605
|
-
error: `Malformed Content-Type header: ${contentType}`
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
// Extract main MIME type (before any parameters like charset)
|
|
609
|
-
const mainType = normalizedContentType.split(';')[0].trim();
|
|
610
|
-
// Security check: Block dangerous MIME types that could bypass validation
|
|
611
|
-
const dangerousMimeTypes = [
|
|
612
|
-
'text/html', // Could contain XSS
|
|
613
|
-
'text/javascript', // Could contain malicious scripts
|
|
614
|
-
'application/javascript', // Could contain malicious scripts
|
|
615
|
-
'text/xml', // Could contain XXE attacks
|
|
616
|
-
'application/xml', // Could contain XXE attacks
|
|
617
|
-
'image/svg+xml', // Could contain XSS in SVG
|
|
618
|
-
'multipart/form-data', // Unexpected for API responses
|
|
619
|
-
'application/x-www-form-urlencoded' // Unexpected for API responses
|
|
620
|
-
];
|
|
621
|
-
if (dangerousMimeTypes.includes(mainType)) {
|
|
622
|
-
SecurityMonitor.logSecurityEvent({
|
|
623
|
-
type: 'CONTENT_INJECTION_ATTEMPT',
|
|
624
|
-
severity: 'HIGH',
|
|
625
|
-
source: 'persona_sharer',
|
|
626
|
-
details: `Dangerous Content-Type detected: ${contentType}`,
|
|
627
|
-
metadata: { contentType, expectedType }
|
|
628
|
-
});
|
|
629
|
-
return {
|
|
630
|
-
isValid: false,
|
|
631
|
-
error: `Dangerous Content-Type not allowed: ${mainType}`
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
// Check if the main type matches expected type
|
|
635
|
-
if (!mainType.includes(normalizedExpectedType)) {
|
|
636
|
-
return {
|
|
637
|
-
isValid: false,
|
|
638
|
-
error: `Content-Type mismatch: expected ${expectedType}, got ${mainType}`
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
// Additional validation for JSON responses
|
|
642
|
-
if (normalizedExpectedType === 'application/json') {
|
|
643
|
-
// Accept various JSON-compatible MIME types
|
|
644
|
-
const acceptableJsonTypes = [
|
|
645
|
-
'application/json',
|
|
646
|
-
'application/vnd.api+json',
|
|
647
|
-
'application/vnd.github.v3+json',
|
|
648
|
-
'text/json' // Some APIs use this (though not standard)
|
|
649
|
-
];
|
|
650
|
-
const isAcceptableJson = acceptableJsonTypes.some(type => mainType === type);
|
|
651
|
-
if (!isAcceptableJson) {
|
|
652
|
-
return {
|
|
653
|
-
isValid: false,
|
|
654
|
-
error: `Unsupported JSON Content-Type: ${mainType}`
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
// Validate charset parameter if present
|
|
658
|
-
const charsetMatch = normalizedContentType.match(/charset=([^;\s]+)/);
|
|
659
|
-
if (charsetMatch) {
|
|
660
|
-
const charset = charsetMatch[1].toLowerCase();
|
|
661
|
-
const supportedCharsets = ['utf-8', 'utf8', 'ascii', 'iso-8859-1'];
|
|
662
|
-
if (!supportedCharsets.includes(charset)) {
|
|
663
|
-
return {
|
|
664
|
-
isValid: false,
|
|
665
|
-
error: `Unsupported charset: ${charset}`
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
// Log successful validation for monitoring
|
|
671
|
-
logger.debug('Content-Type validation passed', {
|
|
672
|
-
contentType: mainType,
|
|
673
|
-
expectedType: normalizedExpectedType
|
|
674
|
-
});
|
|
675
|
-
return { isValid: true };
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVyc29uYVNoYXJlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wZXJzb25hL2V4cG9ydC1pbXBvcnQvUGVyc29uYVNoYXJlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7O0dBWUc7QUFHSCxPQUFPLEVBQUUsZUFBZSxFQUFtQixNQUFNLHNCQUFzQixDQUFDO0FBRXhFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM5RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzFFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3BGLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQVV2RSxNQUFNLE9BQU8sYUFBYTtJQUtkO0lBQ0E7SUFMRixRQUFRLENBQWtCO0lBQzFCLGlCQUFpQixDQUFjO0lBRXZDLFlBQ1UsWUFBMEIsRUFDMUIsV0FBMEI7UUFEMUIsaUJBQVksR0FBWixZQUFZLENBQWM7UUFDMUIsZ0JBQVcsR0FBWCxXQUFXLENBQWU7UUFFbEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLGVBQWUsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUVqRCxrRUFBa0U7UUFDbEUsOERBQThEO1FBQzlELE1BQU0sYUFBYSxHQUFHLFlBQVksQ0FBQyxjQUFjLEVBQUUsS0FBSyxJ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Q3RFLElBQUksWUFBWSxFQUFFLENBQUM7Z0JBQ2pCLE1BQU0sT0FBTyxHQUFHLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDOUMsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLFlBQVksQ0FBQyxDQUFDO2dCQUNuRSxJQUFJLENBQUMsaUJBQWlCLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7b0JBQ3pDLE9BQU87d0JBQ0wsT0FBTyxFQUFFLEtBQUs7d0JBQ2QsS0FBSyxFQUFFLHdCQUF3QixPQUFPLEVBQUU7cUJBQ3pDLENBQUM7Z0JBQ0osQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsMkNBQTJDO1FBQzNDLE1BQU0sQ0FBQyxLQUFLLENBQUMsZ0NBQWdDLEVBQUU7WUFDN0MsV0FBVyxFQUFFLFFBQVE7WUFDckIsWUFBWSxFQUFFLHNCQUFzQjtTQUNyQyxDQUFDLENBQUM7UUFFSCxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO0lBQzNCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUGVyc29uYSBzaGFyaW5nIGZ1bmN0aW9uYWxpdHkgdmlhIFVSTHNcbiAqIFxuICogU0VDVVJJVFkgRklYIElNUExFTUVOVEVEIChEZWZlbnNlLWluLURlcHRoIFZhbGlkYXRpb24pOlxuICogMS4gQ1JJVElDQUw6IEFkZGVkIHZhbGlkYXRlLWJlZm9yZS1yZXR1cm4gcGF0dGVybiBpbiBhbGwgaW1wb3J0IG1ldGhvZHNcbiAqIDIuIEhJR0g6IENvbnRlbnQgc2VjdXJpdHkgdmFsaWRhdGlvbiB1c2luZyBDb250ZW50VmFsaWRhdG9yIGJlZm9yZSBkYXRhIHJldHVyblxuICogMy4gTUVESVVNOiBTaXplIHZhbGlkYXRpb24gdG8gcHJldmVudCBtZW1vcnkgZXhoYXVzdGlvbiBhdHRhY2tzXG4gKiA0LiBNRURJVU06IFN0cnVjdHVyZSB2YWxpZGF0aW9uIHRvIHByZXZlbnQgbWFsZm9ybWVkIGRhdGEgcHJvY2Vzc2luZ1xuICogNS4gREVGRU5TRS1JTi1ERVBUSDogTXVsdGlwbGUgdmFsaWRhdGlvbiBsYXllcnMgYmVmb3JlIFBlcnNvbmFJbXBvcnRlciBwcm9jZXNzaW5nXG4gKiBcbiAqIFRoaXMgcHJvdmlkZXMgZGVmZW5zZS1pbi1kZXB0aCBzZWN1cml0eSBieSB2YWxpZGF0aW5nIGNvbnRlbnQgYXQgdGhlIGVhcmxpZXN0XG4gKiBwb3NzaWJsZSBwb2ludCBiZWZvcmUgYW55IGRhdGEgaXMgcmV0dXJuZWQgdG8gY2FsbGluZyBjb2RlIG9yIGZpbGUgb3BlcmF0aW9ucy5cbiAqL1xuXG5pbXBvcnQgeyBQZXJzb25hIH0gZnJvbSAnLi4vLi4vdHlwZXMvcGVyc29uYS5qcyc7XG5pbXBvcnQgeyBQZXJzb25hRXhwb3J0ZXIsIEV4cG9ydGVkUGVyc29uYSB9IGZyb20gJy4vUGVyc29uYUV4cG9ydGVyLmpzJztcbmltcG9ydCB7IEdpdEh1YkNsaWVudCB9IGZyb20gJy4uLy4uL2NvbGxlY3Rpb24vR2l0SHViQ2xpZW50LmpzJztcbmltcG9ydCB7IFRva2VuTWFuYWdlciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3Rva2VuTWFuYWdlci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eUVycm9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvZXJyb3JzLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgRXJyb3JIYW5kbGVyLCBFcnJvckNhdGVnb3J5IH0gZnJvbSAnLi4vLi4vdXRpbHMvRXJyb3JIYW5kbGVyLmpzJztcbmltcG9ydCB7IFZhbGlkYXRpb25FcnJvckNvZGVzLCBOZXR3b3JrRXJyb3JDb2RlcyB9IGZyb20gJy4uLy4uL3V0aWxzL2Vycm9yQ29kZXMuanMnO1xuaW1wb3J0IHsgUmF0ZUxpbWl0ZXIgfSBmcm9tICcuLi8uLi91dGlscy9SYXRlTGltaXRlci5qcyc7XG5pbXBvcnQgeyBDb250ZW50VmFsaWRhdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvY29udGVudFZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyB2YWxpZGF0ZUNvbnRlbnRTaXplIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvSW5wdXRWYWxpZGF0b3IuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFNoYXJlUmVzdWx0IHtcbiAgc3VjY2VzczogYm9vbGVhbjtcbiAgdXJsPzogc3RyaW5nO1xuICBnaXN0SWQ/OiBzdHJpbmc7XG4gIGV4cGlyZXNBdD86IHN0cmluZztcbiAgbWVzc2FnZTogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgUGVyc29uYVNoYXJlciB7XG4gIHByaXZhdGUgZXhwb3J0ZXI6IFBlcnNvbmFFeHBvcnRlcjtcbiAgcHJpdmF0ZSBnaXRodWJSYXRlTGltaXRlcjogUmF0ZUxpbWl0ZXI7XG4gIFxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGdpdGh1YkNsaWVudDogR2l0SHViQ2xpZW50LFxuICAgIHByaXZhdGUgY3VycmVudFVzZXI6IHN0cmluZyB8IG51bGxcbiAgKSB7XG4gICAgdGhpcy5leHBvcnRlciA9IG5ldyBQZXJzb25hRXhwb3J0ZXIoY3VycmVudFVzZXIpO1xuICAgIFxuICAgIC8vIEdpdEh1YiBBUEkgcmF0ZSBsaW1pdDogNjAgcmVxdWVzdHMgcGVyIGhvdXIgZm9yIHVuYXV0aGVudGljYXRlZFxuICAgIC8vIDUwMDAgcGVyIGhvdXIgZm9yIGF1dGhlbnRpY2F0ZWQgLSB1c2UgVG9rZW5NYW5hZ2VyIHRvIGNoZWNrXG4gICAgY29uc3QgaGFzVmFsaWRUb2tlbiA9IFRva2VuTWFuYWdlci5nZXRHaXRIdWJUb2tlbigpICE9PSBudWxsO1xuICAgIHRoaXMuZ2l0aHViUmF0ZUxpbWl0ZXIgPSBuZXcgUmF0ZUxpbWl0ZXIoe1xuICAgICAgbWF4UmVxdWVzdHM6IGhhc1ZhbGlkVG9rZW4gPyAxMDAgOiAzMCwgLy8gQ29uc2VydmF0aXZlIGxpbWl0c1xuICAgICAgd2luZG93TXM6IDYwICogNjAgKiAxMDAwLCAvLyAxIGhvdXJcbiAgICAgIG1pbkRlbGF5TXM6IDEwMDAgLy8gTWluaW11bSAxIHNlY29uZCBiZXR3ZWVuIHJlcXVlc3RzXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU2hhcmUgYSBwZXJzb25hIHZpYSBHaXRIdWIgR2lzdFxuICAgKi9cbiAgYXN5bmMgc2hhcmVQZXJzb25hKHBlcnNvbmE6IFBlcnNvbmEsIGV4cGlyeURheXM6IG51bWJlciA9IDcpOiBQcm9taXNlPFNoYXJlUmVzdWx0PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFZhbGlkYXRlIGdpc3QgcGVybWlzc2lvbnMgaWYgdG9rZW4gaXMgYXZhaWxhYmxlXG4gICAgICBjb25zdCB0b2tlbiA9IFRva2VuTWFuYWdlci5nZXRHaXRIdWJUb2tlbigpO1xuICAgICAgbGV0IGhhc1ZhbGlkVG9rZW4gPSBmYWxzZTtcbiAgICAgIFxuICAgICAgaWYgKHRva2VuKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgdmFsaWRhdGlvbiA9IGF3YWl0IFRva2VuTWFuYWdlci5lbnN1cmVUb2tlblBlcm1pc3Npb25zKCdnaXN0Jyk7XG4gICAgICAgICAgaWYgKCF2YWxpZGF0aW9uLmlzVmFsaWQpIHtcbiAgICAgICAgICAgIGNvbnN0IHNhZmVNZXNzYWdlID0gVG9rZW5NYW5hZ2VyLmNyZWF0ZVNhZmVFcnJvck1lc3NhZ2UodmFsaWRhdGlvbi5lcnJvciB8fCAnVW5rbm93biB2YWxpZGF0aW9uIGVycm9yJywgdG9rZW4pO1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ0dpdEh1YiB0b2tlbiBsYWNrcyBnaXN0IHBlcm1pc3Npb25zLCBmYWxsaW5nIGJhY2sgdG8gYmFzZTY0IFVSTCcsIHsgZXJyb3I6IHNhZmVNZXNzYWdlIH0pO1xuICAgICAgICAgICAgLy8gQ29udGludWUgdG8gZmFsbGJhY2sgaW5zdGVhZCBvZiBmYWlsaW5nXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGhhc1ZhbGlkVG9rZW4gPSB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAvLyBIYW5kbGUgcmF0ZSBsaW1pdGluZyBvciBvdGhlciBzZWN1cml0eSBlcnJvcnMgZ3JhY2VmdWxseVxuICAgICAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlY3VyaXR5RXJyb3IgJiYgZXJyb3IuY29kZSA9PT0gJ1JBVEVfTElNSVRfRVhDRUVERUQnKSB7XG4gICAgICAgICAgICBsb2dnZXIud2FybignVG9rZW4gdmFsaWRhdGlvbiByYXRlIGxpbWl0ZWQsIGZhbGxpbmcgYmFjayB0byBiYXNlNjQgVVJMJywgeyBcbiAgICAgICAgICAgICAgZXJyb3I6ICdSYXRlIGxpbWl0IGV4Y2VlZGVkIGZvciB0b2tlbiB2YWxpZGF0aW9uJyBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0gZWxzZSBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICAgICAgY29uc3Qgc2FmZU1lc3NhZ2UgPSBUb2tlbk1hbmFnZXIuY3JlYXRlU2FmZUVycm9yTWVzc2FnZShlcnJvci5tZXNzYWdlLCB0b2tlbik7XG4gICAgICAgICAgICBsb2dnZXIud2FybignVG9rZW4gdmFsaWRhdGlvbiBmYWlsZWQsIGZhbGxpbmcgYmFjayB0byBiYXNlNjQgVVJMJywgeyBlcnJvcjogc2FmZU1lc3NhZ2UgfSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIENvbnRpbnVlIHRvIGZhbGxiYWNrIGluc3RlYWQgb2YgZmFpbGluZ1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEV4cG9ydCBwZXJzb25hIHRvIHN0cnVjdHVyZWQgZm9ybWF0XG4gICAgICBjb25zdCBleHBvcnREYXRhID0gdGhpcy5leHBvcnRlci5leHBvcnRQZXJzb25hKHBlcnNvbmEpO1xuICAgICAgXG4gICAgICAvLyBBZGQgc2hhcmluZyBtZXRhZGF0YVxuICAgICAgY29uc3Qgc2hhcmVEYXRhID0ge1xuICAgICAgICAuLi5leHBvcnREYXRhLFxuICAgICAgICBzaGFyZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICBzaGFyZWRCeTogdGhpcy5jdXJyZW50VXNlciB8fCAnYW5vbnltb3VzJyxcbiAgICAgICAgZXhwaXJlc0F0OiBuZXcgRGF0ZShEYXRlLm5vdygpICsgZXhwaXJ5RGF5cyAqIDI0ICogNjAgKiA2MCAqIDEwMDApLnRvSVNPU3RyaW5nKCksXG4gICAgICAgIHNoYXJlVmVyc2lvbjogJzEuMC4wJ1xuICAgICAgfTtcblxuICAgICAgLy8gQ3JlYXRlIEdpdEh1YiBHaXN0IGlmIHRva2VuIGhhcyBwcm9wZXIgcGVybWlzc2lvbnNcbiAgICAgIGlmIChoYXNWYWxpZFRva2VuKSB7XG4gICAgICAgIGNvbnN0IGdpc3RSZXN1bHQgPSBhd2FpdCB0aGlzLmNyZWF0ZUdpc3QocGVyc29uYS5tZXRhZGF0YS5uYW1lLCBzaGFyZURhdGEpO1xuICAgICAgICBcbiAgICAgICAgaWYgKGdpc3RSZXN1bHQuc3VjY2Vzcykge1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgdXJsOiBnaXN0UmVzdWx0LnVybCEsXG4gICAgICAgICAgICBnaXN0SWQ6IGdpc3RSZXN1bHQuZ2lzdElkLFxuICAgICAgICAgICAgZXhwaXJlc0F0OiBzaGFyZURhdGEuZXhwaXJlc0F0LFxuICAgICAgICAgICAgbWVzc2FnZTogdGhpcy5mb3JtYXRTaGFyZVN1Y2Nlc3MoZ2lzdFJlc3VsdC51cmwhLCBzaGFyZURhdGEuZXhwaXJlc0F0KVxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gRmFsbGJhY2sgdG8gYmFzZTY0IFVSTCBpZiBHaXN0IGZhaWxzIG9yIG5vIHRva2VuXG4gICAgICByZXR1cm4gdGhpcy5jcmVhdGVCYXNlNjRVcmwoc2hhcmVEYXRhKTtcblxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcik7XG4gICAgICBjb25zdCBzYWZlTWVzc2FnZSA9IFRva2VuTWFuYWdlci5jcmVhdGVTYWZlRXJyb3JNZXNzYWdlKGVycm9yTWVzc2FnZSk7XG4gICAgICBsb2dnZXIuZXJyb3IoJ1NoYXJlIGVycm9yJywgeyBlcnJvcjogc2FmZU1lc3NhZ2UgfSk7XG4gICAgICBcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBtZXNzYWdlOiBgRmFpbGVkIHRvIHNoYXJlIHBlcnNvbmE6ICR7c2FmZU1lc3NhZ2V9YFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSW1wb3J0IGEgcGVyc29uYSBmcm9tIGEgc2hhcmUgVVJMXG4gICAqIFNFQ1VSSVRZIEZJWDogVmFsaWRhdGUgQUxMIGNvbnRlbnQgYmVmb3JlIHJldHVybmluZyBhbnkgZGF0YVxuICAgKi9cbiAgYXN5bmMgaW1wb3J0RnJvbVVybCh1cmw6IHN0cmluZyk6IFByb21pc2U8eyBzdWNjZXNzOiBib29sZWFuOyBkYXRhPzogYW55OyBtZXNzYWdlOiBzdHJpbmcgfT4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBWYWxpZGF0ZSBVUkwgZmlyc3RcbiAgICAgIGlmICghdGhpcy52YWxpZGF0ZVNoYXJlVXJsKHVybCkpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICBtZXNzYWdlOiAnSW52YWxpZCBvciB1bnNhZmUgVVJMIHByb3ZpZGVkJ1xuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgLy8gQ2hlY2sgaWYgaXQncyBhIEdpdEh1YiBHaXN0IFVSTFxuICAgICAgY29uc3QgZ2lzdElkID0gdGhpcy5leHRyYWN0R2lzdElkKHVybCk7XG4gICAgICBpZiAoZ2lzdElkKSB7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmltcG9ydEZyb21HaXN0KGdpc3RJZCk7XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGlmIGl0J3MgYSBiYXNlNjQgVVJMXG4gICAgICBpZiAodXJsLmluY2x1ZGVzKCcjZG9sbGhvdXNlLXBlcnNvbmE9JykpIHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuaW1wb3J0RnJvbUJhc2U2NFVybCh1cmwpO1xuICAgICAgfVxuXG4gICAgICAvLyBWYWxpZGF0ZSBVUkwgZm9yIHNlY3VyaXR5XG4gICAgICBpZiAoIXRoaXMudmFsaWRhdGVTaGFyZVVybCh1cmwpKSB7XG4gICAgICAgIHRocm93IEVycm9ySGFuZGxlci5jcmVhdGVFcnJvcignSW52YWxpZCBvciBwb3RlbnRpYWxseSBtYWxpY2lvdXMgVVJMJywgRXJyb3JDYXRlZ29yeS5WQUxJREFUSU9OX0VSUk9SLCBWYWxpZGF0aW9uRXJyb3JDb2Rlcy5JTlZBTElEX1VSTCk7XG4gICAgICB9XG5cbiAgICAgIC8vIFRyeSBkaXJlY3QgZmV0Y2ggd2l0aCB0aW1lb3V0XG4gICAgICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICAgICAgY29uc3QgdGltZW91dElkID0gc2V0VGltZW91dCgoKSA9PiBjb250cm9sbGVyLmFib3J0KCksIDUwMDApOyAvLyA1IHNlY29uZCB0aW1lb3V0XG4gICAgICBcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godXJsLCB7XG4gICAgICAgICAgc2lnbmFsOiBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgICAnVXNlci1BZ2VudCc6ICdEb2xsaG91c2VNQ1AvMS4wJyxcbiAgICAgICAgICAgICdBY2NlcHQnOiAnYXBwbGljYXRpb24vanNvbidcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gICAgICAgIFxuICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgdGhyb3cgRXJyb3JIYW5kbGVyLmNyZWF0ZUVycm9yKGBSZXF1ZXN0IGZhaWxlZCB3aXRoIHN0YXR1cyAke3Jlc3BvbnNlLnN0YXR1c31gLCBFcnJvckNhdGVnb3J5Lk5FVFdPUktfRVJST1IsIE5ldHdvcmtFcnJvckNvZGVzLlJFUVVFU1RfRkFJTEVEKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEVOSEFOQ0VEIFNFQ1VSSVRZIEZJWDogQ29tcHJlaGVuc2l2ZSBDb250ZW50LVR5cGUgdmFsaWRhdGlvblxuICAgICAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdjb250ZW50LXR5cGUnKTtcbiAgICAgICAgY29uc3QgY29udGVudFR5cGVWYWxpZGF0aW9uID0gdGhpcy52YWxpZGF0ZUNvbnRlbnRUeXBlKGNvbnRlbnRUeXBlLCAnYXBwbGljYXRpb24vanNvbicpO1xuICAgICAgICBpZiAoIWNvbnRlbnRUeXBlVmFsaWRhdGlvbi5pc1ZhbGlkKSB7XG4gICAgICAgICAgdGhyb3cgRXJyb3JIYW5kbGVyLmNyZWF0ZUVycm9yKFxuICAgICAgICAgICAgYEludmFsaWQgcmVzcG9uc2UgdHlwZTogJHtjb250ZW50VHlwZVZhbGlkYXRpb24uZXJyb3J9YCwgXG4gICAgICAgICAgICBFcnJvckNhdGVnb3J5Lk5FVFdPUktfRVJST1IsIFxuICAgICAgICAgICAgTmV0d29ya0Vycm9yQ29kZXMuSU5WQUxJRF9SRVNQT05TRVxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDaGVjayByZXNwb25zZSBzaXplIHRvIHByZXZlbnQgbWVtb3J5IGV4aGF1c3Rpb25cbiAgICAgICAgY29uc3QgY29udGVudExlbmd0aCA9IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdjb250ZW50LWxlbmd0aCcpO1xuICAgICAgICBjb25zdCBtYXhTaXplID0gNSAqIDEwMjQgKiAxMDI0OyAvLyA1TUIgbWF4XG4gICAgICAgIGlmIChjb250ZW50TGVuZ3RoICYmIHBhcnNlSW50KGNvbnRlbnRMZW5ndGgpID4gbWF4U2l6ZSkge1xuICAgICAgICAgIHRocm93IEVycm9ySGFuZGxlci5jcmVhdGVFcnJvcignUmVzcG9uc2UgdG9vIGxhcmdlJywgRXJyb3JDYXRlZ29yeS5WQUxJREFUSU9OX0VSUk9SLCBOZXR3b3JrRXJyb3JDb2Rlcy5SRVNQT05TRV9UT09fTEFSR0UpO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICAgICAgXG4gICAgICAgIC8vIFNFQ1VSSVRZIEZJWDogVmFsaWRhdGUgY29udGVudCBiZWZvcmUgcmV0dXJuaW5nXG4gICAgICAgIGNvbnN0IGRhdGFWYWxpZGF0aW9uID0gYXdhaXQgdGhpcy52YWxpZGF0ZVBlcnNvbmFEYXRhKGRhdGEpO1xuICAgICAgICBpZiAoIWRhdGFWYWxpZGF0aW9uLmlzVmFsaWQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihgQ29udGVudCB2YWxpZGF0aW9uIGZhaWxlZDogJHtkYXRhVmFsaWRhdGlvbi5lcnJvcn1gKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgIGRhdGEsXG4gICAgICAgICAgbWVzc2FnZTogJ1N1Y2Nlc3NmdWxseSByZXRyaWV2ZWQgcGVyc29uYSBkYXRhJ1xuICAgICAgICB9O1xuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gICAgICB9XG5cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdJbXBvcnQgZnJvbSBVUkwgZXJyb3InLCBlcnJvcik7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgbWVzc2FnZTogYEZhaWxlZCB0byBpbXBvcnQgZnJvbSBVUkw6ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpfWBcbiAgICAgIH07XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIEdpdEh1YiBHaXN0XG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGNyZWF0ZUdpc3QocGVyc29uYU5hbWU6IHN0cmluZywgZGF0YTogYW55KTogUHJvbWlzZTx7IHN1Y2Nlc3M6IGJvb2xlYW47IHVybD86IHN0cmluZzsgZ2lzdElkPzogc3RyaW5nIH0+IHtcbiAgICB0cnkge1xuICAgICAgLy8gVXNlIFRva2VuTWFuYWdlciBmb3Igc2VjdXJlIHRva2VuIGhhbmRsaW5nXG4gICAgICBjb25zdCB0b2tlbiA9IFRva2VuTWFuYWdlci5nZXRHaXRIdWJUb2tlbigpO1xuICAgICAgaWYgKCF0b2tlbikge1xuICAgICAgICBsb2dnZXIuaW5mbygnTm8gdmFsaWQgR2l0SHViIHRva2VuIGF2YWlsYWJsZSBmb3IgR2lzdCBjcmVhdGlvbicpO1xuICAgICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSB9O1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBDaGVjayByYXRlIGxpbWl0XG4gICAgICBjb25zdCByYXRlTGltaXRTdGF0dXMgPSB0aGlzLmdpdGh1YlJhdGVMaW1pdGVyLmNoZWNrTGltaXQoKTtcbiAgICAgIGlmICghcmF0ZUxpbWl0U3RhdHVzLmFsbG93ZWQpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oYEdpdEh1YiBBUEkgcmF0ZSBsaW1pdCBleGNlZWRlZC4gUmV0cnkgYWZ0ZXIgJHtyYXRlTGltaXRTdGF0dXMucmV0cnlBZnRlck1zfW1zYCk7XG4gICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlIH07XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgICBjb25zdCB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KCgpID0+IGNvbnRyb2xsZXIuYWJvcnQoKSwgMTAwMDApOyAvLyAxMCBzZWNvbmQgdGltZW91dFxuICAgICAgXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKCdodHRwczovL2FwaS5naXRodWIuY29tL2dpc3RzJywge1xuICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgICAgIHNpZ25hbDogY29udHJvbGxlci5zaWduYWwsXG4gICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAnQXV0aG9yaXphdGlvbic6IGBCZWFyZXIgJHt0b2tlbn1gLFxuICAgICAgICAgICdBY2NlcHQnOiAnYXBwbGljYXRpb24vdm5kLmdpdGh1Yi52Mytqc29uJyxcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICAgICdVc2VyLUFnZW50JzogJ0RvbGxob3VzZU1DUC8xLjAnXG4gICAgICAgIH0sXG4gICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICBkZXNjcmlwdGlvbjogYERvbGxob3VzZU1DUCBQZXJzb25hOiAke3BlcnNvbmFOYW1lfWAsXG4gICAgICAgICAgcHVibGljOiBmYWxzZSwgLy8gUHJpdmF0ZSBnaXN0IGZvciBzZWN1cml0eVxuICAgICAgICAgIGZpbGVzOiB7XG4gICAgICAgICAgICAncGVyc29uYS5qc29uJzoge1xuICAgICAgICAgICAgICBjb250ZW50OiBKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCAyKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dElkKTtcblxuICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgdGhyb3cgRXJyb3JIYW5kbGVyLmNyZWF0ZUVycm9yKGBHaXRIdWIgQVBJIGVycm9yOiAke3Jlc3BvbnNlLnN0YXR1c31gLCBFcnJvckNhdGVnb3J5Lk5FVFdPUktfRVJST1IsIE5ldHdvcmtFcnJvckNvZGVzLkFQSV9FUlJPUik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBFTkhBTkNFRCBTRUNVUklUWSBGSVg6IENvbXByZWhlbnNpdmUgQ29udGVudC1UeXBlIHZhbGlkYXRpb24gZm9yIEdpdEh1YiBBUElcbiAgICAgICAgY29uc3QgY29udGVudFR5cGUgPSByZXNwb25zZS5oZWFkZXJzLmdldCgnY29udGVudC10eXBlJyk7XG4gICAgICAgIGNvbnN0IGdpc3RDb250ZW50VHlwZVZhbGlkYXRpb24gPSB0aGlzLnZhbGlkYXRlQ29udGVudFR5cGUoY29udGVudFR5cGUsICdhcHBsaWNhdGlvbi9qc29uJyk7XG4gICAgICAgIGlmICghZ2lzdENvbnRlbnRUeXBlVmFsaWRhdGlvbi5pc1ZhbGlkKSB7XG4gICAgICAgICAgdGhyb3cgRXJyb3JIYW5kbGVyLmNyZWF0ZUVycm9yKFxuICAgICAgICAgICAgYEludmFsaWQgR2l0SHViIEFQSSByZXNwb25zZSB0eXBlOiAke2dpc3RDb250ZW50VHlwZVZhbGlkYXRpb24uZXJyb3J9YCwgXG4gICAgICAgICAgICBFcnJvckNhdGVnb3J5Lk5FVFdPUktfRVJST1IsIFxuICAgICAgICAgICAgTmV0d29ya0Vycm9yQ29kZXMuSU5WQUxJRF9SRVNQT05TRVxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBnaXN0ID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICBcbiAgICAgICAgLy8gQ29uc3VtZSB0aGUgcmF0ZSBsaW1pdCB0b2tlbiBhZnRlciBzdWNjZXNzZnVsIHJlcXVlc3RcbiAgICAgICAgdGhpcy5naXRodWJSYXRlTGltaXRlci5jb25zdW1lVG9rZW4oKTtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICB1cmw6IGdpc3QuaHRtbF91cmwsXG4gICAgICAgICAgZ2lzdElkOiBnaXN0LmlkXG4gICAgICAgIH07XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dElkKTtcbiAgICAgIH1cblxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcik7XG4gICAgICBjb25zdCBzYWZlTWVzc2FnZSA9IFRva2VuTWFuYWdlci5jcmVhdGVTYWZlRXJyb3JNZXNzYWdlKGVycm9yTWVzc2FnZSk7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0dpc3QgY3JlYXRpb24gZXJyb3InLCB7IGVycm9yOiBzYWZlTWVzc2FnZSB9KTtcbiAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlIH07XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIGJhc2U2NCBVUkwgKGZhbGxiYWNrKVxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVCYXNlNjRVcmwoZGF0YTogYW55KTogU2hhcmVSZXN1bHQge1xuICAgIGNvbnN0IGJhc2U2NCA9IHRoaXMuZXhwb3J0ZXIudG9CYXNlNjQoZGF0YSk7XG4gICAgY29uc3QgdXJsID0gYGh0dHBzOi8vZG9sbGhvdXNlbWNwLmNvbS9pbXBvcnQjZG9sbGhvdXNlLXBlcnNvbmE9JHtiYXNlNjR9YDtcbiAgICBcbiAgICByZXR1cm4ge1xuICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgIHVybCxcbiAgICAgIGV4cGlyZXNBdDogZGF0YS5leHBpcmVzQXQsXG4gICAgICBtZXNzYWdlOiB0aGlzLmZvcm1hdFNoYXJlU3VjY2Vzcyh1cmwsIGRhdGEuZXhwaXJlc0F0KVxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogSW1wb3J0IGZyb20gR2l0SHViIEdpc3RcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgaW1wb3J0RnJvbUdpc3QoZ2lzdElkOiBzdHJpbmcpOiBQcm9taXNlPHsgc3VjY2VzczogYm9vbGVhbjsgZGF0YT86IGFueTsgbWVzc2FnZTogc3RyaW5nIH0+IHtcbiAgICB0cnkge1xuICAgICAgLy8gQ2hlY2sgcmF0ZSBsaW1pdFxuICAgICAgY29uc3QgcmF0ZUxpbWl0U3RhdHVzID0gdGhpcy5naXRodWJSYXRlTGltaXRlci5jaGVja0xpbWl0KCk7XG4gICAgICBpZiAoIXJhdGVMaW1pdFN0YXR1cy5hbGxvd2VkKSB7XG4gICAgICAgIHRocm93IEVycm9ySGFuZGxlci5jcmVhdGVFcnJvcihgR2l0SHViIEFQSSByYXRlIGxpbWl0IGV4Y2VlZGVkLiBQbGVhc2UgdHJ5IGFnYWluIGluICR7TWF0aC5jZWlsKHJhdGVMaW1pdFN0YXR1cy5yZXRyeUFmdGVyTXMhIC8gMTAwMCl9IHNlY29uZHNgLCBFcnJvckNhdGVnb3J5Lk5FVFdPUktfRVJST1IsIE5ldHdvcmtFcnJvckNvZGVzLlJBVEVfTElNSVRfRVhDRUVERUQpO1xuICAgICAgfVxuICAgICAgXG4gICAgICBjb25zdCBnaXN0VXJsID0gYGh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vZ2lzdHMvJHtnaXN0SWR9YDtcbiAgICAgIFxuICAgICAgLy8gVmFsaWRhdGUgVVJMIChzaG91bGQgYWx3YXlzIHBhc3MgZm9yIEdpdEh1YiBBUEkpXG4gICAgICBpZiAoIXRoaXMudmFsaWRhdGVTaGFyZVVybChnaXN0VXJsKSkge1xuICAgICAgICB0aHJvdyBFcnJvckhhbmRsZXIuY3JlYXRlRXJyb3IoJ0ludmFsaWQgR2l0SHViIEFQSSBVUkwnLCBFcnJvckNhdGVnb3J5LlZBTElEQVRJT05fRVJST1IsIFZhbGlkYXRpb25FcnJvckNvZGVzLklOVkFMSURfVVJMKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgIGNvbnN0IHRpbWVvdXRJZCA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCAxMDAwMCk7IC8vIDEwIHNlY29uZCB0aW1lb3V0IGZvciBBUElcbiAgICAgIFxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChnaXN0VXJsLCB7XG4gICAgICAgICAgc2lnbmFsOiBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgICAnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL3ZuZC5naXRodWIudjMranNvbicsXG4gICAgICAgICAgICAnVXNlci1BZ2VudCc6ICdEb2xsaG91c2VNQ1AvMS4wJ1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dElkKTtcblxuICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgdGhyb3cgRXJyb3JIYW5kbGVyLmNyZWF0ZUVycm9yKGBGYWlsZWQgdG8gZmV0Y2ggZ2lzdDogJHtyZXNwb25zZS5zdGF0dXN9YCwgRXJyb3JDYXRlZ29yeS5ORVRXT1JLX0VSUk9SLCBOZXR3b3JrRXJyb3JDb2Rlcy5GRVRDSF9GQUlMRUQpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRU5IQU5DRUQgU0VDVVJJVFkgRklYOiBDb21wcmVoZW5zaXZlIENvbnRlbnQtVHlwZSB2YWxpZGF0aW9uIGZvciBHaXRIdWIgQVBJXG4gICAgICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVzcG9uc2UuaGVhZGVycy5nZXQoJ2NvbnRlbnQtdHlwZScpO1xuICAgICAgICBjb25zdCBnaXN0Q29udGVudFR5cGVWYWxpZGF0aW9uID0gdGhpcy52YWxpZGF0ZUNvbnRlbnRUeXBlKGNvbnRlbnRUeXBlLCAnYXBwbGljYXRpb24vanNvbicpO1xuICAgICAgICBpZiAoIWdpc3RDb250ZW50VHlwZVZhbGlkYXRpb24uaXNWYWxpZCkge1xuICAgICAgICAgIHRocm93IEVycm9ySGFuZGxlci5jcmVhdGVFcnJvcihcbiAgICAgICAgICAgIGBJbnZhbGlkIEdpdEh1YiBBUEkgcmVzcG9uc2UgdHlwZTogJHtnaXN0Q29udGVudFR5cGVWYWxpZGF0aW9uLmVycm9yfWAsIFxuICAgICAgICAgICAgRXJyb3JDYXRlZ29yeS5ORVRXT1JLX0VSUk9SLCBcbiAgICAgICAgICAgIE5ldHdvcmtFcnJvckNvZGVzLklOVkFMSURfUkVTUE9OU0VcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgZ2lzdCA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICAgICAgY29uc3QgcGVyc29uYUZpbGUgPSBnaXN0LmZpbGVzWydwZXJzb25hLmpzb24nXTtcbiAgICAgICAgXG4gICAgICAgIGlmICghcGVyc29uYUZpbGUpIHtcbiAgICAgICAgICB0aHJvdyBFcnJvckhhbmRsZXIuY3JlYXRlRXJyb3IoJ05vIHBlcnNvbmEgZGF0YSBmb3VuZCBpbiBnaXN0JywgRXJyb3JDYXRlZ29yeS5WQUxJREFUSU9OX0VSUk9SLCBWYWxpZGF0aW9uRXJyb3JDb2Rlcy5JTlZBTElEX0lOUFVUKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGRhdGEgPSBKU09OLnBhcnNlKHBlcnNvbmFGaWxlLmNvbnRlbnQpO1xuICAgICAgICBcbiAgICAgICAgLy8gQ2hlY2sgZXhwaXJ5XG4gICAgICAgIGlmIChkYXRhLmV4cGlyZXNBdCAmJiBuZXcgRGF0ZShkYXRhLmV4cGlyZXNBdCkgPCBuZXcgRGF0ZSgpKSB7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgbWVzc2FnZTogJ1RoaXMgc2hhcmUgbGluayBoYXMgZXhwaXJlZCdcbiAgICAgICAgICB9O1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gU0VDVVJJVFkgRklYOiBWYWxpZGF0ZSBjb250ZW50IGJlZm9yZSByZXR1cm5pbmdcbiAgICAgICAgY29uc3QgZ2lzdERhdGFWYWxpZGF0aW9uID0gYXdhaXQgdGhpcy52YWxpZGF0ZVBlcnNvbmFEYXRhKGRhdGEpO1xuICAgICAgICBpZiAoIWdpc3REYXRhVmFsaWRhdGlvbi5pc1ZhbGlkKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFNlY3VyaXR5RXJyb3IoYENvbnRlbnQgdmFsaWRhdGlvbiBmYWlsZWQ6ICR7Z2lzdERhdGFWYWxpZGF0aW9uLmVycm9yfWApO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ29uc3VtZSB0aGUgcmF0ZSBsaW1pdCB0b2tlbiBhZnRlciBzdWNjZXNzZnVsIHJlcXVlc3RcbiAgICAgICAgdGhpcy5naXRodWJSYXRlTGltaXRlci5jb25zdW1lVG9rZW4oKTtcbiAgICAgICAgXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgICBkYXRhLFxuICAgICAgICAgIG1lc3NhZ2U6ICdTdWNjZXNzZnVsbHkgcmV0cmlldmVkIHBlcnNvbmEgZnJvbSBHaXRIdWInXG4gICAgICAgIH07XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dElkKTtcbiAgICAgIH1cblxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0dpc3QgaW1wb3J0IGVycm9yJywgZXJyb3IpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIG1lc3NhZ2U6IGBGYWlsZWQgdG8gaW1wb3J0IGZyb20gZ2lzdDogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgVVJMIGZvciBzZWN1cml0eSAocHJldmVudCBTU1JGIGF0dGFja3MpXG4gICAqL1xuICBwcml2YXRlIHZhbGlkYXRlU2hhcmVVcmwodXJsOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICB0cnkge1xuICAgICAgLy8gMS4gVVJMIGxlbmd0aCBjaGVjayB0byBwcmV2ZW50IERvU1xuICAgICAgaWYgKHVybC5sZW5ndGggPiAyMDQ4KSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdVUkwgZXhjZWVkcyBtYXhpbXVtIGxlbmd0aCcsIHsgdXJsTGVuZ3RoOiB1cmwubGVuZ3RoIH0pO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBhcnNlZCA9IG5ldyBVUkwodXJsKTtcbiAgICAgIFxuICAgICAgLy8gMi4gUHJvdG9jb2wgY2hlY2sgLSBvbmx5IGFsbG93IGh0dHAvaHR0cHNcbiAgICAgIGlmICghWydodHRwczonLCAnaHR0cDonXS5pbmNsdWRlcyhwYXJzZWQucHJvdG9jb2wpKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdJbnZhbGlkIHByb3RvY29sIGluIFVSTCcsIHsgcHJvdG9jb2w6IHBhcnNlZC5wcm90b2NvbCB9KTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyAzLiBQb3J0IHJlc3RyaWN0aW9ucyAtIGJsb2NrIG5vbi1zdGFuZGFyZCBwb3J0cyB0aGF0IGNvdWxkIGJlIGludGVybmFsIHNlcnZpY2VzXG4gICAgICBjb25zdCBwb3J0ID0gcGFyc2VkLnBvcnQgfHwgKHBhcnNlZC5wcm90b2NvbCA9PT0gJ2h0dHBzOicgPyAnNDQzJyA6ICc4MCcpO1xuICAgICAgY29uc3QgYWxsb3dlZFBvcnRzID0gWyc4MCcsICc0NDMnLCAnODA4MCcsICc4NDQzJ107XG4gICAgICBpZiAoIWFsbG93ZWRQb3J0cy5pbmNsdWRlcyhwb3J0KSkge1xuICAgICAgICBsb2dnZXIud2FybignQmxvY2tlZCBub24tc3RhbmRhcmQgcG9ydCcsIHsgcG9ydCB9KTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyA0LiBIb3N0bmFtZSB2YWxpZGF0aW9uXG4gICAgICBjb25zdCBob3N0bmFtZSA9IHBhcnNlZC5ob3N0bmFtZS50b0xvd2VyQ2FzZSgpO1xuICAgICAgXG4gICAgICAvLyBCbG9jayB2YXJpb3VzIGxvY2FsaG9zdCByZXByZXNlbnRhdGlvbnNcbiAgICAgIGNvbnN0IGJsb2NrZWRIb3N0bmFtZXMgPSBbXG4gICAgICAgICdsb2NhbGhvc3QnLCBcbiAgICAgICAgJ2xvY2FsaG9zdC5sb2NhbGRvbWFpbicsXG4gICAgICAgICcwLjAuMC4wJyxcbiAgICAgICAgJzAnLFxuICAgICAgICAnMHgwJyxcbiAgICAgICAgJzB4MDAwMDAwMDAnLFxuICAgICAgICAnWzo6MV0nLFxuICAgICAgICAnWzo6ZmZmZjoxMjcuMC4wLjFdJyxcbiAgICAgICAgJ1swMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDFdJ1xuICAgICAgXTtcbiAgICAgIFxuICAgICAgaWYgKGJsb2NrZWRIb3N0bmFtZXMuaW5jbHVkZXMoaG9zdG5hbWUpKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdCbG9ja2VkIGxvY2FsaG9zdCBob3N0bmFtZScsIHsgaG9zdG5hbWUgfSk7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQmxvY2sgcHJpdmF0ZSBJUCByYW5nZXMgd2l0aCBjb21wcmVoZW5zaXZlIHBhdHRlcm5zXG4gICAgICBjb25zdCBwcml2YXRlSXBQYXR0ZXJucyA9IFtcbiAgICAgICAgL14xMjdcXC4vLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIExvb3BiYWNrXG4gICAgICAgIC9eMTBcXC4vLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBQcml2YXRlIGNsYXNzIEFcbiAgICAgICAgL14xOTJcXC4xNjhcXC4vLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBQcml2YXRlIGNsYXNzIENcbiAgICAgICAgL14xNzJcXC4oMVs2LTldfDJbMC05XXwzWzAtMV0pXFwuLywgICAgICAgICAgLy8gUHJpdmF0ZSBjbGFzcyBCXG4gICAgICAgIC9eMTY5XFwuMjU0XFwuLywgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTGluay1sb2NhbFxuICAgICAgICAvXmZjMDA6L2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJUHY2IHByaXZhdGVcbiAgICAgICAgL15mZTgwOi9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSVB2NiBsaW5rLWxvY2FsXG4gICAgICAgIC9eOjoxJC8sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElQdjYgbG9vcGJhY2tcbiAgICAgICAgL146OmZmZmY6MD86PzA/Oj8wPzo/MD8kL2ksICAgICAgICAgICAgICAgIC8vIElQdjYgbWFwcGVkIElQdjRcbiAgICAgICAgL14xMDBcXC42WzQtOV1cXC4vLCAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIENhcnJpZXItZ3JhZGUgTkFUXG4gICAgICAgIC9eMTAwXFwuWzctOV1bMC05XVxcLi8sICAgICAgICAgICAgICAgICAgICAgICAvLyBDYXJyaWVyLWdyYWRlIE5BVFxuICAgICAgICAvXjEwMFxcLjFbMC0yXVswLTldXFwuLywgICAgICAgICAgICAgICAgICAgICAgLy8gQ2Fycmllci1ncmFkZSBOQVRcbiAgICAgICAgL14wXFwuLywgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFJlc2VydmVkXG4gICAgICAgIC9eMjU1XFwuMjU1XFwuMjU1XFwuMjU1JC8gICAgICAgICAgICAgICAgICAgICAgLy8gQnJvYWRjYXN0XG4gICAgICBdO1xuICAgICAgXG4gICAgICBpZiAocHJpdmF0ZUlwUGF0dGVybnMuc29tZShwYXR0ZXJuID0+IHBhdHRlcm4udGVzdChob3N0bmFtZSkpKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdCbG9ja2VkIHByaXZhdGUgSVAgcmFuZ2UnLCB7IGhvc3RuYW1lIH0pO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIEJsb2NrIGNsb3VkIG1ldGFkYXRhIGVuZHBvaW50c1xuICAgICAgY29uc3QgbWV0YWRhdGFFbmRwb2ludHMgPSBbXG4gICAgICAgICcxNjkuMjU0LjE2OS4yNTQnLCAgICAgLy8gQVdTL0dDUC9BenVyZVxuICAgICAgICAnbWV0YWRhdGEuZ29vZ2xlLmludGVybmFsJyxcbiAgICAgICAgJ21ldGFkYXRhLmF6dXJlLmNvbScsXG4gICAgICAgICcxMDAuMTAwLjEwMC4yMDAnICAgICAgLy8gQWxpYmFiYSBDbG91ZFxuICAgICAgXTtcbiAgICAgIFxuICAgICAgaWYgKG1ldGFkYXRhRW5kcG9pbnRzLmluY2x1ZGVzKGhvc3RuYW1lKSkge1xuICAgICAgICBsb2dnZXIud2FybignQmxvY2tlZCBjbG91ZCBtZXRhZGF0YSBlbmRwb2ludCcsIHsgaG9zdG5hbWUgfSk7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQmxvY2sgbnVtZXJpYyBJUCByZXByZXNlbnRhdGlvbnMgdGhhdCBjb3VsZCBieXBhc3MgY2hlY2tzXG4gICAgICBpZiAoL15cXGQrJC8udGVzdChob3N0bmFtZSkpIHtcbiAgICAgICAgLy8gQ29udmVydCBkZWNpbWFsIHRvIElQIGFuZCBjaGVja1xuICAgICAgICBjb25zdCBudW0gPSBwYXJzZUludChob3N0bmFtZSwgMTApO1xuICAgICAgICBpZiAobnVtIDw9IDB4RkZGRkZGRkYpIHtcbiAgICAgICAgICBjb25zdCBpcCA9IGAkeyhudW0gPj4+IDI0KSAmIDB4RkZ9LiR7KG51bSA+Pj4gMTYpICYgMHhGRn0uJHsobnVtID4+PiA4KSAmIDB4RkZ9LiR7bnVtICYgMHhGRn1gO1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdCbG9ja2VkIG51bWVyaWMgSVAgcmVwcmVzZW50YXRpb24nLCB7IGhvc3RuYW1lLCByZXNvbHZlZElwOiBpcCB9KTtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQmxvY2sgaGV4IElQIHJlcHJlc2VudGF0aW9uc1xuICAgICAgaWYgKC9eMHhbMC05YS1mXSskL2kudGVzdChob3N0bmFtZSkpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ0Jsb2NrZWQgaGV4IElQIHJlcHJlc2VudGF0aW9uJywgeyBob3N0bmFtZSB9KTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBWYWxpZGF0ZSBkb21haW4gZm9ybWF0IChiYXNpYyBjaGVjaylcbiAgICAgIC8vIEFsbG93IEdpdEh1YiBkb21haW5zIGFuZCBjb21tb24gc2hhcmUgcGxhdGZvcm1zXG4gICAgICBjb25zdCB0cnVzdGVkRG9tYWlucyA9IFtcbiAgICAgICAgJ2dpdGh1Yi5jb20nLFxuICAgICAgICAnZ2lzdC5naXRodWIuY29tJywgXG4gICAgICAgICdhcGkuZ2l0aHViLmNvbScsXG4gICAgICAgICdyYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tJyxcbiAgICAgICAgJ2RvbGxob3VzZW1jcC5jb20nXG4gICAgICBdO1xuICAgICAgXG4gICAgICAvLyBDaGVjayBpZiBpdCdzIGEgdHJ1c3RlZCBkb21haW5cbiAgICAgIGNvbnN0IGlzVHJ1c3RlZERvbWFpbiA9IHRydXN0ZWREb21haW5zLnNvbWUoZG9tYWluID0+IFxuICAgICAgICBob3N0bmFtZSA9PT0gZG9tYWluIHx8IGhvc3RuYW1lLmVuZHNXaXRoKGAuJHtkb21haW59YClcbiAgICAgICk7XG4gICAgICBcbiAgICAgIGlmICghaXNUcnVzdGVkRG9tYWluKSB7XG4gICAgICAgIC8vIEZvciBub24tdHJ1c3RlZCBkb21haW5zLCBhcHBseSBzdHJpY3RlciB2YWxpZGF0aW9uXG4gICAgICAgIC8vIE11c3QgYmUgYSB2YWxpZCBkb21haW4gZm9ybWF0LCBub3QganVzdCBhbiBJUFxuICAgICAgICBjb25zdCBkb21haW5QYXR0ZXJuID0gL14oW2EtejAtOV0rKC1bYS16MC05XSspKlxcLikrW2Etel17Mix9JC9pO1xuICAgICAgICBjb25zdCBpcHY0UGF0dGVybiA9IC9eXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM30kLztcbiAgICAgICAgY29uc3QgaXB2NlBhdHRlcm4gPSAvXlxcWz8oWzAtOWEtZl17MCw0fTopezIsN31bMC05YS1mXXswLDR9XFxdPyQvaTtcbiAgICAgICAgXG4gICAgICAgIGlmIChpcHY0UGF0dGVybi50ZXN0KGhvc3RuYW1lKSB8fCBpcHY2UGF0dGVybi50ZXN0KGhvc3RuYW1lKSkge1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdEaXJlY3QgSVAgYWNjZXNzIG5vdCBhbGxvd2VkIGZvciB1bnRydXN0ZWQgc291cmNlcycsIHsgaG9zdG5hbWUgfSk7XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICBpZiAoIWRvbWFpblBhdHRlcm4udGVzdChob3N0bmFtZSkpIHtcbiAgICAgICAgICBsb2dnZXIud2FybignSW52YWxpZCBkb21haW4gZm9ybWF0JywgeyBob3N0bmFtZSB9KTtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgbG9nZ2VyLmRlYnVnKCdVUkwgdmFsaWRhdGlvbiBwYXNzZWQnLCB7IFxuICAgICAgICBob3N0bmFtZSwgXG4gICAgICAgIHByb3RvY29sOiBwYXJzZWQucHJvdG9jb2wsXG4gICAgICAgIGlzVHJ1c3RlZDogaXNUcnVzdGVkRG9tYWluIFxuICAgICAgfSk7XG4gICAgICBcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIud2FybignVVJMIHZhbGlkYXRpb24gZXJyb3InLCB7IGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJyB9KTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSW1wb3J0IGZyb20gYmFzZTY0IFVSTFxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBpbXBvcnRGcm9tQmFzZTY0VXJsKHVybDogc3RyaW5nKTogUHJvbWlzZTx7IHN1Y2Nlc3M6IGJvb2xlYW47IGRhdGE/OiBhbnk7IG1lc3NhZ2U6IHN0cmluZyB9PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIExpbWl0IGJhc2U2NCBsZW5ndGggdG8gcHJldmVudCBSZURvUyBhdHRhY2tzICgxMEtCIG1heCBmb3IgYmFzZTY0IGVuY29kZWQgZGF0YSlcbiAgICAgIGNvbnN0IG1hdGNoID0gdXJsLm1hdGNoKC8jZG9sbGhvdXNlLXBlcnNvbmE9KFtBLVphLXowLTkrLz1dezEsMTAwMDB9KSQvKTtcbiAgICAgIGlmICghbWF0Y2gpIHtcbiAgICAgICAgdGhyb3cgRXJyb3JIYW5kbGVyLmNyZWF0ZUVycm9yKCdJbnZhbGlkIHNoYXJlIFVSTCBmb3JtYXQnLCBFcnJvckNhdGVnb3J5LlZBTElEQVRJT05fRVJST1IsIFZhbGlkYXRpb25FcnJvckNvZGVzLklOVkFMSURfRk9STUFUKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgYmFzZTY0ID0gbWF0Y2hbMV07XG4gICAgICBjb25zdCBqc29uID0gQnVmZmVyLmZyb20oYmFzZTY0LCAnYmFzZTY0JykudG9TdHJpbmcoJ3V0Zi04Jyk7XG4gICAgICBjb25zdCBkYXRhID0gSlNPTi5wYXJzZShqc29uKTtcblxuICAgICAgLy8gQ2hlY2sgZXhwaXJ5XG4gICAgICBpZiAoZGF0YS5leHBpcmVzQXQgJiYgbmV3IERhdGUoZGF0YS5leHBpcmVzQXQpIDwgbmV3IERhdGUoKSkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIG1lc3NhZ2U6ICdUaGlzIHNoYXJlIGxpbmsgaGFzIGV4cGlyZWQnXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIC8vIFNFQ1VSSVRZIEZJWDogVmFsaWRhdGUgY29udGVudCBiZWZvcmUgcmV0dXJuaW5nXG4gICAgICBjb25zdCBiYXNlNjREYXRhVmFsaWRhdGlvbiA9IGF3YWl0IHRoaXMudmFsaWRhdGVQZXJzb25hRGF0YShkYXRhKTtcbiAgICAgIGlmICghYmFzZTY0RGF0YVZhbGlkYXRpb24uaXNWYWxpZCkge1xuICAgICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihgQ29udGVudCB2YWxpZGF0aW9uIGZhaWxlZDogJHtiYXNlNjREYXRhVmFsaWRhdGlvbi5lcnJvcn1gKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgZGF0YSxcbiAgICAgICAgbWVzc2FnZTogJ1N1Y2Nlc3NmdWxseSBkZWNvZGVkIHBlcnNvbmEgZGF0YSdcbiAgICAgIH07XG5cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIG1lc3NhZ2U6IGBGYWlsZWQgdG8gZGVjb2RlIHNoYXJlIFVSTDogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YFxuICAgICAgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRXh0cmFjdCBHaXN0IElEIGZyb20gR2l0SHViIFVSTFxuICAgKi9cbiAgcHJpdmF0ZSBleHRyYWN0R2lzdElkKHVybDogc3RyaW5nKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgY29uc3QgbWF0Y2ggPSB1cmwubWF0Y2goL2dpc3RcXC5naXRodWJcXC5jb21cXC9bXlxcL10rXFwvKFthLWYwLTldKykvKTtcbiAgICByZXR1cm4gbWF0Y2ggPyBtYXRjaFsxXSA6IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogRm9ybWF0IHNoYXJlIHN1Y2Nlc3MgbWVzc2FnZVxuICAgKi9cbiAgcHJpdmF0ZSBmb3JtYXRTaGFyZVN1Y2Nlc3ModXJsOiBzdHJpbmcsIGV4cGlyZXNBdDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBleHBpcnlEYXRlID0gbmV3IERhdGUoZXhwaXJlc0F0KTtcbiAgICBjb25zdCBkYXlzVW50aWxFeHBpcnkgPSBNYXRoLmNlaWwoKGV4cGlyeURhdGUuZ2V0VGltZSgpIC0gRGF0ZS5ub3coKSkgLyAoMjQgKiA2MCAqIDYwICogMTAwMCkpO1xuXG4gICAgcmV0dXJuIGDinIUgU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgc2hhcmUgbGluayFcblxu8J+UlyBTaGFyZSBVUkw6XG4ke3VybH1cblxu4o+x77iPIEV4cGlyZXM6ICR7ZXhwaXJ5RGF0ZS50b0xvY2FsZURhdGVTdHJpbmcoKX0gKCR7ZGF5c1VudGlsRXhwaXJ5fSBkYXlzKVxuXG7wn5OLIFRvIHNoYXJlIHRoaXMgcGVyc29uYTpcbjEuIENvcHkgdGhlIFVSTCBhYm92ZVxuMi4gU2hhcmUgaXQgd2l0aCBvdGhlcnNcbjMuIFRoZXkgY2FuIGltcG9ydCB1c2luZzogaW1wb3J0X2Zyb21fdXJsIFwiJHt1cmx9XCJcblxu8J+UkiBQcml2YWN5OiBUaGlzIGxpbmsgaXMgcHJpdmF0ZSBhbmQgd2lsbCBleHBpcmUgYXV0b21hdGljYWxseS5gO1xuICB9XG5cbiAgLyoqXG4gICAqIFNFQ1VSSVRZIEZJWDogVmFsaWRhdGUgcGVyc29uYSBkYXRhIGJlZm9yZSBhbnkgcHJvY2Vzc2luZ1xuICAgKiBUaGlzIHByb3ZpZGVzIGRlZmVuc2UtaW4tZGVwdGggdmFsaWRhdGlvbiBiZWZvcmUgY29udGVudCByZWFjaGVzIGZpbGUgb3BlcmF0aW9uc1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2YWxpZGF0ZVBlcnNvbmFEYXRhKGRhdGE6IGFueSk6IFByb21pc2U8eyBpc1ZhbGlkOiBib29sZWFuOyBlcnJvcj86IHN0cmluZyB9PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIEJhc2ljIHN0cnVjdHVyZSB2YWxpZGF0aW9uXG4gICAgICBpZiAoIWRhdGEgfHwgdHlwZW9mIGRhdGEgIT09ICdvYmplY3QnKSB7XG4gICAgICAgIHJldHVybiB7IGlzVmFsaWQ6IGZhbHNlLCBlcnJvcjogJ0ludmFsaWQgZGF0YSBzdHJ1Y3R1cmUnIH07XG4gICAgICB9XG5cbiAgICAgIC8vIFZhbGlkYXRlIHJlcXVpcmVkIGZpZWxkcyBmb3IgcGVyc29uYSBkYXRhXG4gICAgICBpZiAoZGF0YS5tZXRhZGF0YSAmJiAoIWRhdGEubWV0YWRhdGEubmFtZSB8fCAhZGF0YS5tZXRhZGF0YS5kZXNjcmlwdGlvbikpIHtcbiAgICAgICAgcmV0dXJuIHsgaXNWYWxpZDogZmFsc2UsIGVycm9yOiAnTWlzc2luZyByZXF1aXJlZCBwZXJzb25hIG1ldGFkYXRhJyB9O1xuICAgICAgfVxuXG4gICAgICAvLyBWYWxpZGF0ZSBjb250ZW50IGlmIHByZXNlbnRcbiAgICAgIGlmIChkYXRhLmNvbnRlbnQpIHtcbiAgICAgICAgLy8gU2l6ZSB2YWxpZGF0aW9uXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdmFsaWRhdGVDb250ZW50U2l6ZShkYXRhLmNvbnRlbnQsIDEwMCAqIDEwMjQpOyAvLyAxMDBLQiBsaW1pdFxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIHJldHVybiB7IGlzVmFsaWQ6IGZhbHNlLCBlcnJvcjogYENvbnRlbnQgc2l6ZSB2YWxpZGF0aW9uIGZhaWxlZDogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJ31gIH07XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDb250ZW50IHNlY3VyaXR5IHZhbGlkYXRpb25cbiAgICAgICAgY29uc3QgY29udGVudFZhbGlkYXRpb24gPSBDb250ZW50VmFsaWRhdG9yLnZhbGlkYXRlQW5kU2FuaXRpemUoZGF0YS5jb250ZW50KTtcbiAgICAgICAgaWYgKCFjb250ZW50VmFsaWRhdGlvbi5pc1ZhbGlkICYmIGNvbnRlbnRWYWxpZGF0aW9uLnNldmVyaXR5ID09PSAnY3JpdGljYWwnKSB7XG4gICAgICAgICAgcmV0dXJuIHsgXG4gICAgICAgICAgICBpc1ZhbGlkOiBmYWxzZSwgXG4gICAgICAgICAgICBlcnJvcjogYENyaXRpY2FsIHNlY3VyaXR5IHRocmVhdCBkZXRlY3RlZDogJHtjb250ZW50VmFsaWRhdGlvbi5kZXRlY3RlZFBhdHRlcm5zPy5qb2luKCcsICcpfWAgXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBWYWxpZGF0ZSBidW5kbGUgc3RydWN0dXJlIGlmIGl0J3MgYSBidW5kbGVcbiAgICAgIGlmIChkYXRhLnBlcnNvbmFzICYmIEFycmF5LmlzQXJyYXkoZGF0YS5wZXJzb25hcykpIHtcbiAgICAgICAgZm9yIChjb25zdCBwZXJzb25hIG9mIGRhdGEucGVyc29uYXMpIHtcbiAgICAgICAgICBjb25zdCBwZXJzb25hVmFsaWRhdGlvbiA9IGF3YWl0IHRoaXMudmFsaWRhdGVQZXJzb25hRGF0YShwZXJzb25hKTtcbiAgICAgICAgICBpZiAoIXBlcnNvbmFWYWxpZGF0aW9uLmlzVmFsaWQpIHtcbiAgICAgICAgICAgIHJldHVybiB7IGlzVmFsaWQ6IGZhbHNlLCBlcnJvcjogYEJ1bmRsZSB2YWxpZGF0aW9uIGZhaWxlZDogJHtwZXJzb25hVmFsaWRhdGlvbi5lcnJvcn1gIH07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7IGlzVmFsaWQ6IHRydWUgfTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgcmV0dXJuIHsgXG4gICAgICAgIGlzVmFsaWQ6IGZhbHNlLCBcbiAgICAgICAgZXJyb3I6IGBWYWxpZGF0aW9uIGVycm9yOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ1Vua25vd24gZXJyb3InfWAgXG4gICAgICB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFTkhBTkNFRCBTRUNVUklUWSBGSVg6IENvbXByZWhlbnNpdmUgQ29udGVudC1UeXBlIHZhbGlkYXRpb25cbiAgICogU3RyZW5ndGhlbnMgTUlNRSB0eXBlIHZhbGlkYXRpb24gd2l0aCBjb21wcmVoZW5zaXZlIHNlY3VyaXR5IGNoZWNrc1xuICAgKi9cbiAgcHJpdmF0ZSB2YWxpZGF0ZUNvbnRlbnRUeXBlKFxuICAgIGNvbnRlbnRUeXBlOiBzdHJpbmcgfCBudWxsLCBcbiAgICBleHBlY3RlZFR5cGU6IHN0cmluZ1xuICApOiB7IGlzVmFsaWQ6IGJvb2xlYW47IGVycm9yPzogc3RyaW5nIH0ge1xuICAgIC8vIENoZWNrIGlmIENvbnRlbnQtVHlwZSBoZWFkZXIgZXhpc3RzXG4gICAgaWYgKCFjb250ZW50VHlwZSkge1xuICAgICAgcmV0dXJuIHsgXG4gICAgICAgIGlzVmFsaWQ6IGZhbHNlLCBcbiAgICAgICAgZXJyb3I6ICdNaXNzaW5nIENvbnRlbnQtVHlwZSBoZWFkZXInIFxuICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBOb3JtYWxpemUgYW5kIHNhbml0aXplIHRoZSBjb250ZW50IHR5cGVcbiAgICBjb25zdCBub3JtYWxpemVkQ29udGVudFR5cGUgPSBjb250ZW50VHlwZS50b0xvd2VyQ2FzZSgpLnRyaW0oKTtcbiAgICBjb25zdCBub3JtYWxpemVkRXhwZWN0ZWRUeXBlID0gZXhwZWN0ZWRUeXBlLnRvTG93ZXJDYXNlKCkudHJpbSgpO1xuXG4gICAgLy8gVmFsaWRhdGUgQ29udGVudC1UeXBlIGZvcm1hdCAoYmFzaWMgTUlNRSB0eXBlIHN0cnVjdHVyZSlcbiAgICBjb25zdCBtaW1lVHlwZVBhdHRlcm4gPSAvXlthLXowLTldW2EtejAtOSEjJCZcXC1cXF5fXSpcXC9bYS16MC05XVthLXowLTkhIyQmXFwtXFxeX10qKD86XFxzKjsuKik/JC87XG4gICAgaWYgKCFtaW1lVHlwZVBhdHRlcm4udGVzdChub3JtYWxpemVkQ29udGVudFR5cGUpKSB7XG4gICAgICByZXR1cm4geyBcbiAgICAgICAgaXNWYWxpZDogZmFsc2UsIFxuICAgICAgICBlcnJvcjogYE1hbGZvcm1lZCBDb250ZW50LVR5cGUgaGVhZGVyOiAke2NvbnRlbnRUeXBlfWAgXG4gICAgICB9O1xuICAgIH1cblxuICAgIC8vIEV4dHJhY3QgbWFpbiBNSU1FIHR5cGUgKGJlZm9yZSBhbnkgcGFyYW1ldGVycyBsaWtlIGNoYXJzZXQpXG4gICAgY29uc3QgbWFpblR5cGUgPSBub3JtYWxpemVkQ29udGVudFR5cGUuc3BsaXQoJzsnKVswXS50cmltKCk7XG4gICAgXG4gICAgLy8gU2VjdXJpdHkgY2hlY2s6IEJsb2NrIGRhbmdlcm91cyBNSU1FIHR5cGVzIHRoYXQgY291bGQgYnlwYXNzIHZhbGlkYXRpb25cbiAgICBjb25zdCBkYW5nZXJvdXNNaW1lVHlwZXMgPSBbXG4gICAgICAndGV4dC9odG1sJywgICAgICAgICAgIC8vIENvdWxkIGNvbnRhaW4gWFNTXG4gICAgICAndGV4dC9qYXZhc2NyaXB0JywgICAgIC8vIENvdWxkIGNvbnRhaW4gbWFsaWNpb3VzIHNjcmlwdHNcbiAgICAgICdhcHBsaWNhdGlvbi9qYXZhc2NyaXB0JywgLy8gQ291bGQgY29udGFpbiBtYWxpY2lvdXMgc2NyaXB0c1xuICAgICAgJ3RleHQveG1sJywgICAgICAgICAgICAvLyBDb3VsZCBjb250YWluIFhYRSBhdHRhY2tzXG4gICAgICAnYXBwbGljYXRpb24veG1sJywgICAgIC8vIENvdWxkIGNvbnRhaW4gWFhFIGF0dGFja3NcbiAgICAgICdpbWFnZS9zdmcreG1sJywgICAgICAgLy8gQ291bGQgY29udGFpbiBYU1MgaW4gU1ZHXG4gICAgICAnbXVsdGlwYXJ0L2Zvcm0tZGF0YScsIC8vIFVuZXhwZWN0ZWQgZm9yIEFQSSByZXNwb25zZXNcbiAgICAgICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnIC8vIFVuZXhwZWN0ZWQgZm9yIEFQSSByZXNwb25zZXNcbiAgICBdO1xuXG4gICAgaWYgKGRhbmdlcm91c01pbWVUeXBlcy5pbmNsdWRlcyhtYWluVHlwZSkpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ0NPTlRFTlRfSU5KRUNUSU9OX0FUVEVNUFQnLFxuICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICBzb3VyY2U6ICdwZXJzb25hX3NoYXJlcicsXG4gICAgICAgIGRldGFpbHM6IGBEYW5nZXJvdXMgQ29udGVudC1UeXBlIGRldGVjdGVkOiAke2NvbnRlbnRUeXBlfWAsXG4gICAgICAgIG1ldGFkYXRhOiB7IGNvbnRlbnRUeXBlLCBleHBlY3RlZFR5cGUgfVxuICAgICAgfSk7XG4gICAgICByZXR1cm4geyBcbiAgICAgICAgaXNWYWxpZDogZmFsc2UsIFxuICAgICAgICBlcnJvcjogYERhbmdlcm91cyBDb250ZW50LVR5cGUgbm90IGFsbG93ZWQ6ICR7bWFpblR5cGV9YCBcbiAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgaWYgdGhlIG1haW4gdHlwZSBtYXRjaGVzIGV4cGVjdGVkIHR5cGVcbiAgICBpZiAoIW1haW5UeXBlLmluY2x1ZGVzKG5vcm1hbGl6ZWRFeHBlY3RlZFR5cGUpKSB7XG4gICAgICByZXR1cm4geyBcbiAgICAgICAgaXNWYWxpZDogZmFsc2UsIFxuICAgICAgICBlcnJvcjogYENvbnRlbnQtVHlwZSBtaXNtYXRjaDogZXhwZWN0ZWQgJHtleHBlY3RlZFR5cGV9LCBnb3QgJHttYWluVHlwZX1gIFxuICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBBZGRpdGlvbmFsIHZhbGlkYXRpb24gZm9yIEpTT04gcmVzcG9uc2VzXG4gICAgaWYgKG5vcm1hbGl6ZWRFeHBlY3RlZFR5cGUgPT09ICdhcHBsaWNhdGlvbi9qc29uJykge1xuICAgICAgLy8gQWNjZXB0IHZhcmlvdXMgSlNPTi1jb21wYXRpYmxlIE1JTUUgdHlwZXNcbiAgICAgIGNvbnN0IGFjY2VwdGFibGVKc29uVHlwZXMgPSBbXG4gICAgICAgICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgICAgJ2FwcGxpY2F0aW9uL3ZuZC5hcGkranNvbicsXG4gICAgICAgICdhcHBsaWNhdGlvbi92bmQuZ2l0aHViLnYzK2pzb24nLFxuICAgICAgICAndGV4dC9qc29uJyAvLyBTb21lIEFQSXMgdXNlIHRoaXMgKHRob3VnaCBub3Qgc3RhbmRhcmQpXG4gICAgICBdO1xuICAgICAgXG4gICAgICBjb25zdCBpc0FjY2VwdGFibGVKc29uID0gYWNjZXB0YWJsZUpzb25UeXBlcy5zb21lKHR5cGUgPT4gbWFpblR5cGUgPT09IHR5cGUpO1xuICAgICAgaWYgKCFpc0FjY2VwdGFibGVKc29uKSB7XG4gICAgICAgIHJldHVybiB7IFxuICAgICAgICAgIGlzVmFsaWQ6IGZhbHNlLCBcbiAgICAgICAgICBlcnJvcjogYFVuc3VwcG9ydGVkIEpTT04gQ29udGVudC1UeXBlOiAke21haW5UeXBlfWAgXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIC8vIFZhbGlkYXRlIGNoYXJzZXQgcGFyYW1ldGVyIGlmIHByZXNlbnRcbiAgICAgIGNvbnN0IGNoYXJzZXRNYXRjaCA9IG5vcm1hbGl6ZWRDb250ZW50VHlwZS5tYXRjaCgvY2hhcnNldD0oW147XFxzXSspLyk7XG4gICAgICBpZiAoY2hhcnNldE1hdGNoKSB7XG4gICAgICAgIGNvbnN0IGNoYXJzZXQgPSBjaGFyc2V0TWF0Y2hbMV0udG9Mb3dlckNhc2UoKTtcbiAgICAgICAgY29uc3Qgc3VwcG9ydGVkQ2hhcnNldHMgPSBbJ3V0Zi04JywgJ3V0ZjgnLCAnYXNjaWknLCAnaXNvLTg4NTktMSddO1xuICAgICAgICBpZiAoIXN1cHBvcnRlZENoYXJzZXRzLmluY2x1ZGVzKGNoYXJzZXQpKSB7XG4gICAgICAgICAgcmV0dXJuIHsgXG4gICAgICAgICAgICBpc1ZhbGlkOiBmYWxzZSwgXG4gICAgICAgICAgICBlcnJvcjogYFVuc3VwcG9ydGVkIGNoYXJzZXQ6ICR7Y2hhcnNldH1gIFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBMb2cgc3VjY2Vzc2Z1bCB2YWxpZGF0aW9uIGZvciBtb25pdG9yaW5nXG4gICAgbG9nZ2VyLmRlYnVnKCdDb250ZW50LVR5cGUgdmFsaWRhdGlvbiBwYXNzZWQnLCB7IFxuICAgICAgY29udGVudFR5cGU6IG1haW5UeXBlLCBcbiAgICAgIGV4cGVjdGVkVHlwZTogbm9ybWFsaXplZEV4cGVjdGVkVHlwZSBcbiAgICB9KTtcblxuICAgIHJldHVybiB7IGlzVmFsaWQ6IHRydWUgfTtcbiAgfVxufSJdfQ==
|