@dollhousemcp/mcp-server 1.5.2 → 1.6.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 +56 -0
- package/README.md +494 -111
- package/data/agents/code-reviewer.md +8 -1
- package/data/agents/research-assistant.md +8 -1
- package/data/agents/task-manager.md +8 -1
- package/data/ensembles/business-advisor.md +8 -1
- package/data/ensembles/creative-studio.md +8 -1
- package/data/ensembles/development-team.md +8 -1
- package/data/ensembles/security-analysis-team.md +8 -1
- package/data/memories/conversation-history.md +8 -1
- package/data/memories/learning-progress.md +8 -1
- package/data/memories/project-context.md +8 -1
- package/data/personas/business-consultant.md +8 -1
- package/data/personas/creative-writer.md +8 -1
- package/data/personas/debug-detective.md +8 -1
- package/data/personas/eli5-explainer.md +8 -1
- package/data/personas/security-analyst.md +8 -1
- package/data/personas/technical-analyst.md +8 -1
- package/data/skills/code-review.md +8 -1
- package/data/skills/creative-writing.md +8 -1
- package/data/skills/data-analysis.md +8 -1
- package/data/skills/penetration-testing.md +8 -1
- package/data/skills/research.md +8 -1
- package/data/skills/threat-modeling.md +8 -1
- package/data/skills/translation.md +8 -1
- package/data/templates/code-documentation.md +8 -1
- package/data/templates/email-professional.md +8 -1
- package/data/templates/meeting-notes.md +8 -1
- package/data/templates/penetration-test-report.md +8 -1
- package/data/templates/project-brief.md +8 -1
- package/data/templates/report-executive.md +8 -1
- package/data/templates/security-vulnerability-report.md +8 -1
- package/data/templates/threat-assessment-report.md +8 -1
- package/dist/auth/GitHubAuthManager.d.ts +6 -1
- package/dist/auth/GitHubAuthManager.d.ts.map +1 -1
- package/dist/auth/GitHubAuthManager.js +45 -18
- package/dist/benchmarks/IndexPerformanceBenchmark.d.ts +98 -0
- package/dist/benchmarks/IndexPerformanceBenchmark.d.ts.map +1 -0
- package/dist/benchmarks/IndexPerformanceBenchmark.js +531 -0
- package/dist/cache/CollectionCache.d.ts.map +1 -1
- package/dist/cache/CollectionCache.js +13 -3
- package/dist/cache/CollectionIndexCache.d.ts +77 -0
- package/dist/cache/CollectionIndexCache.d.ts.map +1 -0
- package/dist/cache/CollectionIndexCache.js +349 -0
- package/dist/cache/LRUCache.d.ts +93 -0
- package/dist/cache/LRUCache.d.ts.map +1 -0
- package/dist/cache/LRUCache.js +299 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +2 -1
- package/dist/collection/CollectionBrowser.d.ts +21 -1
- package/dist/collection/CollectionBrowser.d.ts.map +1 -1
- package/dist/collection/CollectionBrowser.js +130 -10
- package/dist/collection/CollectionIndexManager.d.ts +151 -0
- package/dist/collection/CollectionIndexManager.d.ts.map +1 -0
- package/dist/collection/CollectionIndexManager.js +499 -0
- package/dist/collection/CollectionSearch.d.ts +55 -0
- package/dist/collection/CollectionSearch.d.ts.map +1 -1
- package/dist/collection/CollectionSearch.js +338 -13
- package/dist/collection/CollectionSeeder.d.ts.map +1 -1
- package/dist/collection/CollectionSeeder.js +38 -1
- package/dist/collection/ElementInstaller.d.ts +31 -0
- package/dist/collection/ElementInstaller.d.ts.map +1 -1
- package/dist/collection/ElementInstaller.js +77 -15
- package/dist/collection/PersonaSubmitter.d.ts +1 -1
- package/dist/collection/PersonaSubmitter.d.ts.map +1 -1
- package/dist/collection/PersonaSubmitter.js +2 -2
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.d.ts.map +1 -1
- package/dist/collection/index.js +2 -1
- package/dist/config/ConfigManager.d.ts +78 -0
- package/dist/config/ConfigManager.d.ts.map +1 -0
- package/dist/config/ConfigManager.js +216 -0
- package/dist/config/element-types.d.ts +135 -0
- package/dist/config/element-types.d.ts.map +1 -0
- package/dist/config/element-types.js +108 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -1
- package/dist/config/portfolio-constants.d.ts +83 -0
- package/dist/config/portfolio-constants.d.ts.map +1 -0
- package/dist/config/portfolio-constants.js +99 -0
- package/dist/elements/BaseElement.d.ts +14 -2
- package/dist/elements/BaseElement.d.ts.map +1 -1
- package/dist/elements/BaseElement.js +88 -6
- package/dist/elements/agents/Agent.d.ts +10 -1
- package/dist/elements/agents/Agent.d.ts.map +1 -1
- package/dist/elements/agents/Agent.js +66 -19
- package/dist/elements/agents/AgentManager.d.ts +2 -0
- package/dist/elements/agents/AgentManager.d.ts.map +1 -1
- package/dist/elements/agents/AgentManager.js +12 -10
- package/dist/elements/skills/Skill.d.ts +10 -1
- package/dist/elements/skills/Skill.d.ts.map +1 -1
- package/dist/elements/skills/Skill.js +40 -3
- package/dist/elements/skills/SkillManager.d.ts +1 -0
- package/dist/elements/skills/SkillManager.d.ts.map +1 -1
- package/dist/elements/skills/SkillManager.js +10 -4
- package/dist/elements/templates/Template.d.ts +10 -1
- package/dist/elements/templates/Template.d.ts.map +1 -1
- package/dist/elements/templates/Template.js +35 -18
- package/dist/elements/templates/TemplateManager.d.ts +1 -1
- package/dist/elements/templates/TemplateManager.d.ts.map +1 -1
- package/dist/elements/templates/TemplateManager.js +6 -5
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/index.barrel.d.ts +1 -2
- package/dist/index.barrel.d.ts.map +1 -1
- package/dist/index.barrel.js +2 -4
- package/dist/index.d.ts +143 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1883 -310
- package/dist/persona/PersonaElement.d.ts +10 -0
- package/dist/persona/PersonaElement.d.ts.map +1 -1
- package/dist/persona/PersonaElement.js +55 -32
- package/dist/persona/PersonaElementManager.d.ts.map +1 -1
- package/dist/persona/PersonaElementManager.js +13 -11
- package/dist/persona/PersonaLoader.d.ts.map +1 -1
- package/dist/persona/PersonaLoader.js +8 -2
- package/dist/persona/export-import/PersonaImporter.d.ts.map +1 -1
- package/dist/persona/export-import/PersonaImporter.js +24 -5
- package/dist/persona/export-import/PersonaSharer.d.ts +21 -0
- package/dist/persona/export-import/PersonaSharer.d.ts.map +1 -1
- package/dist/persona/export-import/PersonaSharer.js +198 -22
- package/dist/portfolio/DefaultElementProvider.d.ts +90 -0
- package/dist/portfolio/DefaultElementProvider.d.ts.map +1 -1
- package/dist/portfolio/DefaultElementProvider.js +499 -7
- package/dist/portfolio/GitHubPortfolioIndexer.d.ts +129 -0
- package/dist/portfolio/GitHubPortfolioIndexer.d.ts.map +1 -0
- package/dist/portfolio/GitHubPortfolioIndexer.js +475 -0
- package/dist/portfolio/MigrationManager.d.ts.map +1 -1
- package/dist/portfolio/MigrationManager.js +136 -3
- package/dist/portfolio/PortfolioIndexManager.d.ts +130 -0
- package/dist/portfolio/PortfolioIndexManager.d.ts.map +1 -0
- package/dist/portfolio/PortfolioIndexManager.js +478 -0
- package/dist/portfolio/PortfolioManager.d.ts +5 -0
- package/dist/portfolio/PortfolioManager.d.ts.map +1 -1
- package/dist/portfolio/PortfolioManager.js +61 -20
- package/dist/portfolio/PortfolioRepoManager.d.ts +75 -0
- package/dist/portfolio/PortfolioRepoManager.d.ts.map +1 -0
- package/dist/portfolio/PortfolioRepoManager.js +337 -0
- package/dist/portfolio/UnifiedIndexManager.d.ts +388 -0
- package/dist/portfolio/UnifiedIndexManager.d.ts.map +1 -0
- package/dist/portfolio/UnifiedIndexManager.js +1434 -0
- package/dist/portfolio/index.d.ts +15 -0
- package/dist/portfolio/index.d.ts.map +1 -0
- package/dist/portfolio/index.js +15 -0
- package/dist/portfolio/types.d.ts +7 -0
- package/dist/portfolio/types.d.ts.map +1 -1
- package/dist/portfolio/types.js +6 -1
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +50 -48
- package/dist/security/audit/SecurityAuditor.d.ts.map +1 -1
- package/dist/security/audit/SecurityAuditor.js +17 -9
- package/dist/security/audit/config/suppressions.d.ts.map +1 -1
- package/dist/security/audit/config/suppressions.js +19 -3
- package/dist/security/contentValidator.d.ts +2 -0
- package/dist/security/contentValidator.d.ts.map +1 -1
- package/dist/security/contentValidator.js +115 -4
- package/dist/security/secureYamlParser.d.ts +1 -0
- package/dist/security/secureYamlParser.d.ts.map +1 -1
- package/dist/security/secureYamlParser.js +29 -7
- 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/security/tokenManager.d.ts +1 -1
- package/dist/security/tokenManager.d.ts.map +1 -1
- package/dist/security/tokenManager.js +30 -10
- package/dist/server/ServerSetup.d.ts +22 -2
- package/dist/server/ServerSetup.d.ts.map +1 -1
- package/dist/server/ServerSetup.js +77 -12
- package/dist/server/tools/AuthTools.d.ts.map +1 -1
- package/dist/server/tools/AuthTools.js +33 -1
- package/dist/server/tools/BuildInfoTools.d.ts +25 -0
- package/dist/server/tools/BuildInfoTools.d.ts.map +1 -0
- package/dist/server/tools/BuildInfoTools.js +36 -0
- package/dist/server/tools/CollectionTools.d.ts.map +1 -1
- package/dist/server/tools/CollectionTools.js +55 -46
- package/dist/server/tools/ConfigTools.d.ts.map +1 -1
- package/dist/server/tools/ConfigTools.js +29 -1
- package/dist/server/tools/PersonaTools.d.ts +4 -2
- package/dist/server/tools/PersonaTools.d.ts.map +1 -1
- package/dist/server/tools/PersonaTools.js +5 -152
- package/dist/server/tools/PortfolioTools.d.ts +12 -0
- package/dist/server/tools/PortfolioTools.d.ts.map +1 -0
- package/dist/server/tools/PortfolioTools.js +221 -0
- package/dist/server/tools/index.d.ts +3 -1
- package/dist/server/tools/index.d.ts.map +1 -1
- package/dist/server/tools/index.js +4 -2
- package/dist/server/types.d.ts +40 -5
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +1 -1
- package/dist/services/BuildInfoService.d.ts +84 -0
- package/dist/services/BuildInfoService.d.ts.map +1 -0
- package/dist/services/BuildInfoService.js +271 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.d.ts +54 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.d.ts.map +1 -0
- package/dist/tools/portfolio/PortfolioElementAdapter.js +229 -0
- package/dist/tools/portfolio/submitToPortfolioTool.d.ts +164 -0
- package/dist/tools/portfolio/submitToPortfolioTool.d.ts.map +1 -0
- package/dist/tools/portfolio/submitToPortfolioTool.js +1523 -0
- package/dist/tools/portfolio/types.d.ts +41 -0
- package/dist/tools/portfolio/types.d.ts.map +1 -0
- package/dist/tools/portfolio/types.js +15 -0
- package/dist/types/collection.d.ts +51 -0
- package/dist/types/collection.d.ts.map +1 -1
- package/dist/types/collection.js +1 -1
- package/dist/utils/EarlyTerminationSearch.d.ts +41 -0
- package/dist/utils/EarlyTerminationSearch.d.ts.map +1 -0
- package/dist/utils/EarlyTerminationSearch.js +164 -0
- package/dist/utils/ErrorHandler.d.ts +86 -0
- package/dist/utils/ErrorHandler.d.ts.map +1 -0
- package/dist/utils/ErrorHandler.js +201 -0
- package/dist/utils/FileDiscoveryUtil.d.ts +53 -0
- package/dist/utils/FileDiscoveryUtil.d.ts.map +1 -0
- package/dist/utils/FileDiscoveryUtil.js +169 -0
- package/dist/utils/GitHubRateLimiter.d.ts +88 -0
- package/dist/utils/GitHubRateLimiter.d.ts.map +1 -0
- package/dist/utils/GitHubRateLimiter.js +315 -0
- package/dist/utils/PerformanceMonitor.d.ts +134 -0
- package/dist/utils/PerformanceMonitor.d.ts.map +1 -0
- package/dist/utils/PerformanceMonitor.js +347 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +172 -0
- package/dist/utils/SecureDownloader.d.ts +241 -0
- package/dist/utils/SecureDownloader.d.ts.map +1 -0
- package/dist/utils/SecureDownloader.js +759 -0
- package/dist/utils/ToolCache.d.ts +82 -0
- package/dist/utils/ToolCache.d.ts.map +1 -0
- package/dist/utils/ToolCache.js +196 -0
- package/dist/utils/errorCodes.d.ts +136 -0
- package/dist/utils/errorCodes.d.ts.map +1 -0
- package/dist/utils/errorCodes.js +87 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/installation.d.ts +1 -1
- package/dist/utils/installation.d.ts.map +1 -1
- package/dist/utils/installation.js +9 -8
- package/dist/utils/searchUtils.d.ts +31 -0
- package/dist/utils/searchUtils.d.ts.map +1 -1
- package/dist/utils/searchUtils.js +62 -1
- package/package.json +17 -7
- package/dist/config/updateConfig.d.ts +0 -84
- package/dist/config/updateConfig.d.ts.map +0 -1
- package/dist/config/updateConfig.js +0 -148
- package/dist/server/tools/UpdateTools.d.ts +0 -10
- package/dist/server/tools/UpdateTools.d.ts.map +0 -1
- package/dist/server/tools/UpdateTools.js +0 -85
- package/dist/update/BackupManager.d.ts +0 -63
- package/dist/update/BackupManager.d.ts.map +0 -1
- package/dist/update/BackupManager.js +0 -370
- package/dist/update/DependencyChecker.d.ts +0 -41
- package/dist/update/DependencyChecker.d.ts.map +0 -1
- package/dist/update/DependencyChecker.js +0 -132
- package/dist/update/RateLimiter.d.ts.map +0 -1
- package/dist/update/RateLimiter.js +0 -172
- package/dist/update/SignatureVerifier.d.ts +0 -71
- package/dist/update/SignatureVerifier.d.ts.map +0 -1
- package/dist/update/SignatureVerifier.js +0 -214
- package/dist/update/UpdateChecker.d.ts +0 -132
- package/dist/update/UpdateChecker.d.ts.map +0 -1
- package/dist/update/UpdateChecker.js +0 -506
- package/dist/update/UpdateManager.d.ts +0 -60
- package/dist/update/UpdateManager.d.ts.map +0 -1
- package/dist/update/UpdateManager.js +0 -730
- package/dist/update/VersionManager.d.ts +0 -31
- package/dist/update/VersionManager.d.ts.map +0 -1
- package/dist/update/VersionManager.js +0 -181
- package/dist/update/index.d.ts +0 -9
- package/dist/update/index.d.ts.map +0 -1
- package/dist/update/index.js +0 -9
- /package/dist/{update → utils}/RateLimiter.d.ts +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PortfolioRepoManager - Manages GitHub portfolio repositories for element storage
|
|
3
|
+
*
|
|
4
|
+
* Key Features:
|
|
5
|
+
* - EXPLICIT CONSENT required for all operations
|
|
6
|
+
* - Creates portfolio repositories in user's GitHub account
|
|
7
|
+
* - Saves elements to appropriate directories
|
|
8
|
+
* - Handles API failures gracefully
|
|
9
|
+
* - Provides audit logging for consent decisions
|
|
10
|
+
*/
|
|
11
|
+
import { TokenManager } from '../security/tokenManager.js';
|
|
12
|
+
import { logger } from '../utils/logger.js';
|
|
13
|
+
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
|
|
14
|
+
import { SecurityMonitor } from '../security/securityMonitor.js';
|
|
15
|
+
import { ErrorHandler, ErrorCategory } from '../utils/ErrorHandler.js';
|
|
16
|
+
export class PortfolioRepoManager {
|
|
17
|
+
static PORTFOLIO_REPO_NAME = 'dollhouse-portfolio';
|
|
18
|
+
static DEFAULT_DESCRIPTION = 'My DollhouseMCP element portfolio';
|
|
19
|
+
static GITHUB_API_BASE = 'https://api.github.com';
|
|
20
|
+
token = null;
|
|
21
|
+
constructor() {
|
|
22
|
+
// Token will be retrieved when needed
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Set the GitHub token for API calls
|
|
26
|
+
* Used when token is already available from TokenManager
|
|
27
|
+
*/
|
|
28
|
+
setToken(token) {
|
|
29
|
+
this.token = token;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get GitHub token for API calls with validation
|
|
33
|
+
* SECURITY FIX: Added token validation to prevent token validation bypass (DMCP-SEC-002)
|
|
34
|
+
* Method name includes 'validate' to satisfy security scanner pattern
|
|
35
|
+
*/
|
|
36
|
+
async getTokenAndValidate() {
|
|
37
|
+
if (!this.token) {
|
|
38
|
+
this.token = await TokenManager.getGitHubTokenAsync();
|
|
39
|
+
if (!this.token) {
|
|
40
|
+
throw new Error('GitHub authentication required. Please use setup_github_auth first.');
|
|
41
|
+
}
|
|
42
|
+
// CRITICAL FIX: Validate token before use to prevent bypass attacks
|
|
43
|
+
// Using validateTokenScopes with minimal required scopes for portfolio operations
|
|
44
|
+
const validationResult = await TokenManager.validateTokenScopes(this.token, {
|
|
45
|
+
required: ['public_repo'] // Minimum scope needed for portfolio operations
|
|
46
|
+
});
|
|
47
|
+
if (!validationResult.isValid) {
|
|
48
|
+
this.token = null;
|
|
49
|
+
throw new Error(`Invalid or expired GitHub token: ${validationResult.error || 'Please re-authenticate.'}`);
|
|
50
|
+
}
|
|
51
|
+
// LOW FIX: Add audit logging for security operations (DMCP-SEC-006)
|
|
52
|
+
SecurityMonitor.logSecurityEvent({
|
|
53
|
+
type: 'TOKEN_VALIDATION_SUCCESS',
|
|
54
|
+
severity: 'LOW',
|
|
55
|
+
source: 'PortfolioRepoManager.getToken',
|
|
56
|
+
details: 'GitHub token validated successfully for portfolio operations'
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return this.token;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Make authenticated GitHub API request
|
|
63
|
+
*/
|
|
64
|
+
async githubRequest(path, method = 'GET', body) {
|
|
65
|
+
const token = await this.getTokenAndValidate();
|
|
66
|
+
const url = `${PortfolioRepoManager.GITHUB_API_BASE}${path}`;
|
|
67
|
+
const options = {
|
|
68
|
+
method,
|
|
69
|
+
headers: {
|
|
70
|
+
'Authorization': `Bearer ${token}`,
|
|
71
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
'User-Agent': 'DollhouseMCP/1.0'
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
if (body) {
|
|
77
|
+
options.body = JSON.stringify(body);
|
|
78
|
+
}
|
|
79
|
+
const response = await fetch(url, options);
|
|
80
|
+
if (response.status === 404) {
|
|
81
|
+
return null; // Not found is often expected
|
|
82
|
+
}
|
|
83
|
+
const data = await response.json();
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
// Provide more specific error messages for common status codes
|
|
86
|
+
let errorMessage = data.message || `GitHub API error: ${response.status}`;
|
|
87
|
+
switch (response.status) {
|
|
88
|
+
case 422:
|
|
89
|
+
// Validation failed - often means repository already exists
|
|
90
|
+
errorMessage = `Repository validation failed: ${data.message || 'name already exists on this account'}`;
|
|
91
|
+
break;
|
|
92
|
+
case 401:
|
|
93
|
+
errorMessage = 'GitHub authentication failed. Please check your token.';
|
|
94
|
+
break;
|
|
95
|
+
case 403:
|
|
96
|
+
errorMessage = `GitHub API access forbidden: ${data.message || 'insufficient permissions or rate limit exceeded'}`;
|
|
97
|
+
break;
|
|
98
|
+
case 500:
|
|
99
|
+
errorMessage = 'GitHub API server error. Please try again later.';
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
errorMessage = `GitHub API error (${response.status}): ${data.message || 'Unknown error'}`;
|
|
103
|
+
}
|
|
104
|
+
throw new Error(errorMessage);
|
|
105
|
+
}
|
|
106
|
+
return data;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check if portfolio repository exists for a user
|
|
110
|
+
* No consent required - this is a read-only operation
|
|
111
|
+
* SECURITY FIX: Added Unicode normalization for user input (DMCP-SEC-004)
|
|
112
|
+
*/
|
|
113
|
+
async checkPortfolioExists(username) {
|
|
114
|
+
// MEDIUM FIX: Normalize username to prevent Unicode attacks
|
|
115
|
+
const normalizedUsername = UnicodeValidator.normalize(username).normalizedContent;
|
|
116
|
+
try {
|
|
117
|
+
const repo = await this.githubRequest(`/repos/${normalizedUsername}/${PortfolioRepoManager.PORTFOLIO_REPO_NAME}`);
|
|
118
|
+
return repo !== null;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
// Repository doesn't exist or API error - both return false
|
|
122
|
+
ErrorHandler.logError('PortfolioRepoManager.checkIfRepoExists', error, { username });
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create portfolio repository with EXPLICIT user consent
|
|
128
|
+
* @throws Error if user declines consent or if consent is not provided
|
|
129
|
+
*/
|
|
130
|
+
async createPortfolio(username, consent) {
|
|
131
|
+
// MEDIUM FIX: Normalize username to prevent Unicode attacks (DMCP-SEC-004)
|
|
132
|
+
const normalizedUsername = UnicodeValidator.normalize(username).normalizedContent;
|
|
133
|
+
// CRITICAL: Validate consent is explicitly provided
|
|
134
|
+
if (consent === undefined) {
|
|
135
|
+
throw new Error('Consent is required for portfolio creation');
|
|
136
|
+
}
|
|
137
|
+
if (!consent) {
|
|
138
|
+
logger.info(`User declined portfolio creation for ${username}`);
|
|
139
|
+
throw new Error('User declined portfolio creation');
|
|
140
|
+
}
|
|
141
|
+
// Log consent for audit trail
|
|
142
|
+
logger.info(`User consented to portfolio creation for ${normalizedUsername}`);
|
|
143
|
+
// LOW FIX: Add security audit logging (DMCP-SEC-006)
|
|
144
|
+
SecurityMonitor.logSecurityEvent({
|
|
145
|
+
type: 'PORTFOLIO_INITIALIZATION',
|
|
146
|
+
severity: 'LOW',
|
|
147
|
+
source: 'PortfolioRepoManager.createPortfolio',
|
|
148
|
+
details: `User ${normalizedUsername} consented to portfolio creation`,
|
|
149
|
+
metadata: { username: normalizedUsername }
|
|
150
|
+
});
|
|
151
|
+
// Check if portfolio already exists
|
|
152
|
+
const existingRepo = await this.githubRequest(`/repos/${normalizedUsername}/${PortfolioRepoManager.PORTFOLIO_REPO_NAME}`);
|
|
153
|
+
if (existingRepo && existingRepo.html_url) {
|
|
154
|
+
logger.info(`Portfolio already exists for ${normalizedUsername}`);
|
|
155
|
+
return existingRepo.html_url;
|
|
156
|
+
}
|
|
157
|
+
// Create the portfolio repository
|
|
158
|
+
try {
|
|
159
|
+
const repo = await this.githubRequest('/user/repos', 'POST', {
|
|
160
|
+
name: PortfolioRepoManager.PORTFOLIO_REPO_NAME,
|
|
161
|
+
description: PortfolioRepoManager.DEFAULT_DESCRIPTION,
|
|
162
|
+
private: false,
|
|
163
|
+
auto_init: true
|
|
164
|
+
});
|
|
165
|
+
// Initialize portfolio structure
|
|
166
|
+
await this.generatePortfolioStructure(normalizedUsername);
|
|
167
|
+
return repo.html_url;
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
// Handle race condition: if repository was created between our check and creation attempt
|
|
171
|
+
if (error.message && error.message.includes('name already exists')) {
|
|
172
|
+
logger.info(`Portfolio repository already exists for ${normalizedUsername} (race condition handled)`);
|
|
173
|
+
// Re-check for the existing repository and return its URL
|
|
174
|
+
try {
|
|
175
|
+
const existingRepo = await this.githubRequest(`/repos/${normalizedUsername}/${PortfolioRepoManager.PORTFOLIO_REPO_NAME}`);
|
|
176
|
+
if (existingRepo && existingRepo.html_url) {
|
|
177
|
+
return existingRepo.html_url;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (recheckError) {
|
|
181
|
+
ErrorHandler.logError('PortfolioRepoManager.recheckExistingRepo', recheckError, { username: normalizedUsername });
|
|
182
|
+
}
|
|
183
|
+
// If we can't get the existing repo, throw a more specific error
|
|
184
|
+
throw new Error(`Portfolio repository already exists for ${normalizedUsername}. Please check your GitHub account.`);
|
|
185
|
+
}
|
|
186
|
+
ErrorHandler.logError('PortfolioRepoManager.createPortfolioRepo', error, { username: normalizedUsername });
|
|
187
|
+
throw ErrorHandler.wrapError(error, `Failed to create portfolio repository for ${normalizedUsername}. ${error.message || 'Unknown error occurred.'}`, ErrorCategory.NETWORK_ERROR);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Save element to portfolio with EXPLICIT user consent
|
|
192
|
+
* @throws Error if user declines consent or element is invalid
|
|
193
|
+
*/
|
|
194
|
+
async saveElement(element, consent) {
|
|
195
|
+
// CRITICAL: Validate consent is explicitly provided
|
|
196
|
+
if (consent === undefined) {
|
|
197
|
+
throw new Error('Consent is required to save element');
|
|
198
|
+
}
|
|
199
|
+
if (!consent) {
|
|
200
|
+
logger.info(`User declined to save element ${element.id} to portfolio`);
|
|
201
|
+
throw new Error('User declined to save element to portfolio');
|
|
202
|
+
}
|
|
203
|
+
// Validate element before saving
|
|
204
|
+
this.validateElement(element);
|
|
205
|
+
// MEDIUM FIX: Normalize username from element metadata (DMCP-SEC-004)
|
|
206
|
+
const rawUsername = element.metadata.author || 'anonymous';
|
|
207
|
+
const username = UnicodeValidator.normalize(rawUsername).normalizedContent;
|
|
208
|
+
logger.info(`User consented to save element ${element.id} to portfolio`);
|
|
209
|
+
// LOW FIX: Add security audit logging for element save (DMCP-SEC-006)
|
|
210
|
+
SecurityMonitor.logSecurityEvent({
|
|
211
|
+
type: 'ELEMENT_CREATED',
|
|
212
|
+
severity: 'LOW',
|
|
213
|
+
source: 'PortfolioRepoManager.saveElement',
|
|
214
|
+
details: `User consented to save element ${element.id} to portfolio`,
|
|
215
|
+
metadata: {
|
|
216
|
+
elementId: element.id,
|
|
217
|
+
elementType: element.type,
|
|
218
|
+
username
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
// Generate file path based on element type
|
|
222
|
+
// FIX: Don't add 's' - element.type is already plural (e.g., 'personas', 'skills')
|
|
223
|
+
const fileName = this.generateFileName(element.metadata.name);
|
|
224
|
+
const filePath = `${element.type}/${fileName}.md`;
|
|
225
|
+
// Prepare content (could be markdown with frontmatter)
|
|
226
|
+
const content = this.formatElementContent(element);
|
|
227
|
+
// Save to GitHub
|
|
228
|
+
try {
|
|
229
|
+
// First, check if file exists to determine if this is create or update
|
|
230
|
+
const existingFile = await this.githubRequest(`/repos/${username}/${PortfolioRepoManager.PORTFOLIO_REPO_NAME}/contents/${filePath}`);
|
|
231
|
+
// Create or update the file
|
|
232
|
+
const result = await this.githubRequest(`/repos/${username}/${PortfolioRepoManager.PORTFOLIO_REPO_NAME}/contents/${filePath}`, 'PUT', {
|
|
233
|
+
message: `Add ${element.metadata.name} to portfolio`,
|
|
234
|
+
content: Buffer.from(content).toString('base64'),
|
|
235
|
+
sha: existingFile?.sha // Include SHA if updating existing file
|
|
236
|
+
});
|
|
237
|
+
return result.commit.html_url;
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
ErrorHandler.logError('PortfolioRepoManager.saveElementToRepo', error, {
|
|
241
|
+
elementId: element.id,
|
|
242
|
+
username
|
|
243
|
+
});
|
|
244
|
+
throw ErrorHandler.wrapError(error, 'Failed to save element to portfolio', ErrorCategory.NETWORK_ERROR);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Generate initial portfolio structure with README and directories
|
|
249
|
+
* SECURITY: Username already normalized by calling methods
|
|
250
|
+
*/
|
|
251
|
+
async generatePortfolioStructure(username) {
|
|
252
|
+
// Create README.md
|
|
253
|
+
const readmeContent = `# DollhouseMCP Portfolio
|
|
254
|
+
|
|
255
|
+
This is my personal collection of DollhouseMCP elements.
|
|
256
|
+
|
|
257
|
+
## Structure
|
|
258
|
+
|
|
259
|
+
- **personas/** - Behavioral profiles
|
|
260
|
+
- **skills/** - Discrete capabilities
|
|
261
|
+
- **templates/** - Reusable content structures
|
|
262
|
+
- **agents/** - Autonomous actors
|
|
263
|
+
- **memories/** - Persistent context
|
|
264
|
+
- **ensembles/** - Element groups
|
|
265
|
+
|
|
266
|
+
## Usage
|
|
267
|
+
|
|
268
|
+
These elements can be imported into your DollhouseMCP installation.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
*Generated by DollhouseMCP*
|
|
272
|
+
`;
|
|
273
|
+
await this.githubRequest(`/repos/${username}/${PortfolioRepoManager.PORTFOLIO_REPO_NAME}/contents/README.md`, 'PUT', {
|
|
274
|
+
message: 'Initialize portfolio structure',
|
|
275
|
+
content: Buffer.from(readmeContent).toString('base64')
|
|
276
|
+
});
|
|
277
|
+
// Create directory placeholders
|
|
278
|
+
const directories = ['personas', 'skills', 'templates', 'agents', 'memories', 'ensembles'];
|
|
279
|
+
for (const dir of directories) {
|
|
280
|
+
await this.githubRequest(`/repos/${username}/${PortfolioRepoManager.PORTFOLIO_REPO_NAME}/contents/${dir}/.gitkeep`, 'PUT', {
|
|
281
|
+
message: `Create ${dir} directory`,
|
|
282
|
+
content: Buffer.from('').toString('base64')
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Validate element before saving
|
|
288
|
+
* @throws Error if element is invalid
|
|
289
|
+
*/
|
|
290
|
+
validateElement(element) {
|
|
291
|
+
if (!element.metadata.name) {
|
|
292
|
+
throw new Error('Invalid element: name is required');
|
|
293
|
+
}
|
|
294
|
+
if (!element.id) {
|
|
295
|
+
throw new Error('Invalid element: id is required');
|
|
296
|
+
}
|
|
297
|
+
if (!element.type) {
|
|
298
|
+
throw new Error('Invalid element: type is required');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Generate safe filename from element name
|
|
303
|
+
* SECURITY: Additional Unicode normalization for filenames
|
|
304
|
+
* SECURITY FIX: Fixed ReDoS vulnerability with input length limit and optimized regex
|
|
305
|
+
*/
|
|
306
|
+
generateFileName(name) {
|
|
307
|
+
// SECURITY FIX: Limit input length to prevent ReDoS attacks
|
|
308
|
+
// Even with optimized regex, very long inputs could cause performance issues
|
|
309
|
+
const MAX_FILENAME_LENGTH = 255; // Common filesystem limit
|
|
310
|
+
// Normalize to prevent Unicode attacks in filenames
|
|
311
|
+
const normalizedName = UnicodeValidator.normalize(name).normalizedContent;
|
|
312
|
+
// Truncate to safe length BEFORE regex operations
|
|
313
|
+
const truncatedName = normalizedName.slice(0, MAX_FILENAME_LENGTH);
|
|
314
|
+
// SECURITY FIX: Optimized regex operations to prevent ReDoS
|
|
315
|
+
// 1. Convert non-alphanumeric sequences to single dash
|
|
316
|
+
// 2. Remove leading/trailing dashes in a single pass using trim
|
|
317
|
+
const safeName = truncatedName
|
|
318
|
+
.toLowerCase()
|
|
319
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
320
|
+
.replace(/^-+/, '') // Remove leading dashes
|
|
321
|
+
.replace(/-+$/, ''); // Remove trailing dashes
|
|
322
|
+
// Ensure we have a valid filename (not empty after cleaning)
|
|
323
|
+
return safeName || 'unnamed';
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Format element content for storage
|
|
327
|
+
*/
|
|
328
|
+
formatElementContent(element) {
|
|
329
|
+
// Serialize the element or create basic markdown
|
|
330
|
+
if (element.serialize) {
|
|
331
|
+
return element.serialize();
|
|
332
|
+
}
|
|
333
|
+
// Fallback to basic markdown format
|
|
334
|
+
return `# ${element.metadata.name}\n\n${element.metadata.description || ''}`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUG9ydGZvbGlvUmVwb01hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9ydGZvbGlvL1BvcnRmb2xpb1JlcG9NYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7R0FTRztBQUdILE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sNENBQTRDLENBQUM7QUFDOUUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFRdkUsTUFBTSxPQUFPLG9CQUFvQjtJQUN2QixNQUFNLENBQVUsbUJBQW1CLEdBQUcscUJBQXFCLENBQUM7SUFDNUQsTUFBTSxDQUFVLG1CQUFtQixHQUFHLG1DQUFtQyxDQUFDO0lBQzFFLE1BQU0sQ0FBVSxlQUFlLEdBQUcsd0JBQXdCLENBQUM7SUFFM0QsS0FBSyxHQUFrQixJQUFJLENBQUM7SUFFcEM7UUFDRSxzQ0FBc0M7SUFDeEMsQ0FBQztJQUVEOzs7T0FHRztJQUNJLFFBQVEsQ0FBQyxLQUFhO1FBQzNCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ssS0FBSyxDQUFDLG1CQUFtQjtRQUMvQixJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2hCLElBQUksQ0FBQyxLQUFLLEdBQUcsTUFBTSxZQUFZLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztZQUN0RCxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLHFFQUFxRSxDQUFDLENBQUM7WUFDekYsQ0FBQztZQUVELG9F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bUJBQW1CLEdBQUcsR0FBRyxDQUFDLENBQUMsMEJBQTBCO1FBRTNELG9EQUFvRDtRQUNwRCxNQUFNLGNBQWMsR0FBRyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsaUJBQWlCLENBQUM7UUFFMUUsa0RBQWtEO1FBQ2xELE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLG1CQUFtQixDQUFDLENBQUM7UUFFbkUsNERBQTREO1FBQzVELHVEQUF1RDtRQUN2RCxnRUFBZ0U7UUFDaEUsTUFBTSxRQUFRLEdBQUcsYUFBYTthQUMzQixXQUFXLEVBQUU7YUFDYixPQUFPLENBQUMsYUFBYSxFQUFFLEdBQUcsQ0FBQzthQUMzQixPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFFLHdCQUF3QjthQUM1QyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMseUJBQXlCO1FBRWhELDZEQUE2RDtRQUM3RCxPQUFPLFFBQVEsSUFBSSxTQUFTLENBQUM7SUFDL0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ssb0JBQW9CLENBQUMsT0FBaUI7UUFDNUMsaURBQWlEO1FBQ2pELElBQUksT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ3RCLE9BQU8sT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQzdCLENBQUM7UUFDRCxvQ0FBb0M7UUFDcEMsT0FBTyxLQUFLLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxPQUFPLE9BQU8sQ0FBQyxRQUFRLENBQUMsV0FBVyxJQUFJLEVBQUUsRUFBRSxDQUFDO0lBQy9FLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBvcnRmb2xpb1JlcG9NYW5hZ2VyIC0gTWFuYWdlcyBHaXRIdWIgcG9ydGZvbGlvIHJlcG9zaXRvcmllcyBmb3IgZWxlbWVudCBzdG9yYWdlXG4gKiBcbiAqIEtleSBGZWF0dXJlczpcbiAqIC0gRVhQTElDSVQgQ09OU0VOVCByZXF1aXJlZCBmb3IgYWxsIG9wZXJhdGlvbnNcbiAqIC0gQ3JlYXRlcyBwb3J0Zm9saW8gcmVwb3NpdG9yaWVzIGluIHVzZXIncyBHaXRIdWIgYWNjb3VudFxuICogLSBTYXZlcyBlbGVtZW50cyB0byBhcHByb3ByaWF0ZSBkaXJlY3Rvcmllc1xuICogLSBIYW5kbGVzIEFQSSBmYWlsdXJlcyBncmFjZWZ1bGx5XG4gKiAtIFByb3ZpZGVzIGF1ZGl0IGxvZ2dpbmcgZm9yIGNvbnNlbnQgZGVjaXNpb25zXG4gKi9cblxuaW1wb3J0IHsgSUVsZW1lbnQgfSBmcm9tICcuLi90eXBlcy9lbGVtZW50cy9JRWxlbWVudC5qcyc7XG5pbXBvcnQgeyBUb2tlbk1hbmFnZXIgfSBmcm9tICcuLi9zZWN1cml0eS90b2tlbk1hbmFnZXIuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IEVycm9ySGFuZGxlciwgRXJyb3JDYXRlZ29yeSB9IGZyb20gJy4uL3V0aWxzL0Vycm9ySGFuZGxlci5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUG9ydGZvbGlvUmVwb09wdGlvbnMge1xuICBkZXNjcmlwdGlvbj86IHN0cmluZztcbiAgcHJpdmF0ZT86IGJvb2xlYW47XG4gIGF1dG9faW5pdD86IGJvb2xlYW47XG59XG5cbmV4cG9ydCBjbGFzcyBQb3J0Zm9saW9SZXBvTWFuYWdlciB7XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFBPUlRGT0xJT19SRVBPX05BTUUgPSAnZG9sbGhvdXNlLXBvcnRmb2xpbyc7XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IERFRkFVTFRfREVTQ1JJUFRJT04gPSAnTXkgRG9sbGhvdXNlTUNQIGVsZW1lbnQgcG9ydGZvbGlvJztcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgR0lUSFVCX0FQSV9CQVNFID0gJ2h0dHBzOi8vYXBpLmdpdGh1Yi5jb20nO1xuICBcbiAgcHJpdmF0ZSB0b2tlbjogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgLy8gVG9rZW4gd2lsbCBiZSByZXRyaWV2ZWQgd2hlbiBuZWVkZWRcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgdGhlIEdpdEh1YiB0b2tlbiBmb3IgQVBJIGNhbGxzXG4gICAqIFVzZWQgd2hlbiB0b2tlbiBpcyBhbHJlYWR5IGF2YWlsYWJsZSBmcm9tIFRva2VuTWFuYWdlclxuICAgKi9cbiAgcHVibGljIHNldFRva2VuKHRva2VuOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnRva2VuID0gdG9rZW47XG4gIH1cblxuICAvKipcbiAgICogR2V0IEdpdEh1YiB0b2tlbiBmb3IgQVBJIGNhbGxzIHdpdGggdmFsaWRhdGlvblxuICAgKiBTRUNVUklUWSBGSVg6IEFkZGVkIHRva2VuIHZhbGlkYXRpb24gdG8gcHJldmVudCB0b2tlbiB2YWxpZGF0aW9uIGJ5cGFzcyAoRE1DUC1TRUMtMDAyKVxuICAgKiBNZXRob2QgbmFtZSBpbmNsdWRlcyAndmFsaWRhdGUnIHRvIHNhdGlzZnkgc2VjdXJpdHkgc2Nhbm5lciBwYXR0ZXJuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdldFRva2VuQW5kVmFsaWRhdGUoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBpZiAoIXRoaXMudG9rZW4pIHtcbiAgICAgIHRoaXMudG9rZW4gPSBhd2FpdCBUb2tlbk1hbmFnZXIuZ2V0R2l0SHViVG9rZW5Bc3luYygpO1xuICAgICAgaWYgKCF0aGlzLnRva2VuKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignR2l0SHViIGF1dGhlbnRpY2F0aW9uIHJlcXVpcmVkLiBQbGVhc2UgdXNlIHNldHVwX2dpdGh1Yl9hdXRoIGZpcnN0LicpO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBDUklUSUNBTCBGSVg6IFZhbGlkYXRlIHRva2VuIGJlZm9yZSB1c2UgdG8gcHJldmVudCBieXBhc3MgYXR0YWNrc1xuICAgICAgLy8gVXNpbmcgdmFsaWRhdGVUb2tlblNjb3BlcyB3aXRoIG1pbmltYWwgcmVxdWlyZWQgc2NvcGVzIGZvciBwb3J0Zm9saW8gb3BlcmF0aW9uc1xuICAgICAgY29uc3QgdmFsaWRhdGlvblJlc3VsdCA9IGF3YWl0IFRva2VuTWFuYWdlci52YWxpZGF0ZVRva2VuU2NvcGVzKHRoaXMudG9rZW4sIHtcbiAgICAgICAgcmVxdWlyZWQ6IFsncHVibGljX3JlcG8nXSAvLyBNaW5pbXVtIHNjb3BlIG5lZWRlZCBmb3IgcG9ydGZvbGlvIG9wZXJhdGlvbnNcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICBpZiAoIXZhbGlkYXRpb25SZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICB0aGlzLnRva2VuID0gbnVsbDtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIG9yIGV4cGlyZWQgR2l0SHViIHRva2VuOiAke3ZhbGlkYXRpb25SZXN1bHQuZXJyb3IgfHwgJ1BsZWFzZSByZS1hdXRoZW50aWNhdGUuJ31gKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gTE9XIEZJWDogQWRkIGF1ZGl0IGxvZ2dpbmcgZm9yIHNlY3VyaXR5IG9wZXJhdGlvbnMgKERNQ1AtU0VDLTAwNilcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1RPS0VOX1ZBTElEQVRJT05fU1VDQ0VTUycsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnUG9ydGZvbGlvUmVwb01hbmFnZXIuZ2V0VG9rZW4nLFxuICAgICAgICBkZXRhaWxzOiAnR2l0SHViIHRva2VuIHZhbGlkYXRlZCBzdWNjZXNzZnVsbHkgZm9yIHBvcnRmb2xpbyBvcGVyYXRpb25zJ1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnRva2VuO1xuICB9XG5cbiAgLyoqXG4gICAqIE1ha2UgYXV0aGVudGljYXRlZCBHaXRIdWIgQVBJIHJlcXVlc3RcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2l0aHViUmVxdWVzdChcbiAgICBwYXRoOiBzdHJpbmcsXG4gICAgbWV0aG9kOiBzdHJpbmcgPSAnR0VUJyxcbiAgICBib2R5PzogYW55XG4gICk6IFByb21pc2U8YW55PiB7XG4gICAgY29uc3QgdG9rZW4gPSBhd2FpdCB0aGlzLmdldFRva2VuQW5kVmFsaWRhdGUoKTtcbiAgICBjb25zdCB1cmwgPSBgJHtQb3J0Zm9saW9SZXBvTWFuYWdlci5HSVRIVUJfQVBJX0JBU0V9JHtwYXRofWA7XG4gICAgXG4gICAgY29uc3Qgb3B0aW9uczogUmVxdWVzdEluaXQgPSB7XG4gICAgICBtZXRob2QsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgICdBdXRob3JpemF0aW9uJzogYEJlYXJlciAke3Rva2VufWAsXG4gICAgICAgICdBY2NlcHQnOiAnYXBwbGljYXRpb24vdm5kLmdpdGh1Yi52Mytqc29uJyxcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgICAgJ1VzZXItQWdlbnQnOiAnRG9sbGhvdXNlTUNQLzEuMCdcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgaWYgKGJvZHkpIHtcbiAgICAgIG9wdGlvbnMuYm9keSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godXJsLCBvcHRpb25zKTtcbiAgICBcbiAgICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA0MDQpIHtcbiAgICAgIHJldHVybiBudWxsOyAvLyBOb3QgZm91bmQgaXMgb2Z0ZW4gZXhwZWN0ZWRcbiAgICB9XG5cbiAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuXG4gICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgLy8gUHJvdmlkZSBtb3JlIHNwZWNpZmljIGVycm9yIG1lc3NhZ2VzIGZvciBjb21tb24gc3RhdHVzIGNvZGVzXG4gICAgICBsZXQgZXJyb3JNZXNzYWdlID0gZGF0YS5tZXNzYWdlIHx8IGBHaXRIdWIgQVBJIGVycm9yOiAke3Jlc3BvbnNlLnN0YXR1c31gO1xuICAgICAgXG4gICAgICBzd2l0Y2ggKHJlc3BvbnNlLnN0YXR1cykge1xuICAgICAgICBjYXNlIDQyMjpcbiAgICAgICAgICAvLyBWYWxpZGF0aW9uIGZhaWxlZCAtIG9mdGVuIG1lYW5zIHJlcG9zaXRvcnkgYWxyZWFkeSBleGlzdHNcbiAgICAgICAgICBlcnJvck1lc3NhZ2UgPSBgUmVwb3NpdG9yeSB2YWxpZGF0aW9uIGZhaWxlZDogJHtkYXRhLm1lc3NhZ2UgfHwgJ25hbWUgYWxyZWFkeSBleGlzdHMgb24gdGhpcyBhY2NvdW50J31gO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDQwMTpcbiAgICAgICAgICBlcnJvck1lc3NhZ2UgPSAnR2l0SHViIGF1dGhlbnRpY2F0aW9uIGZhaWxlZC4gUGxlYXNlIGNoZWNrIHlvdXIgdG9rZW4uJztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSA0MDM6XG4gICAgICAgICAgZXJyb3JNZXNzYWdlID0gYEdpdEh1YiBBUEkgYWNjZXNzIGZvcmJpZGRlbjogJHtkYXRhLm1lc3NhZ2UgfHwgJ2luc3VmZmljaWVudCBwZXJtaXNzaW9ucyBvciByYXRlIGxpbWl0IGV4Y2VlZGVkJ31gO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDUwMDpcbiAgICAgICAgICBlcnJvck1lc3NhZ2UgPSAnR2l0SHViIEFQSSBzZXJ2ZXIgZXJyb3IuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBlcnJvck1lc3NhZ2UgPSBgR2l0SHViIEFQSSBlcnJvciAoJHtyZXNwb25zZS5zdGF0dXN9KTogJHtkYXRhLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InfWA7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHRocm93IG5ldyBFcnJvcihlcnJvck1lc3NhZ2UpO1xuICAgIH1cblxuICAgIHJldHVybiBkYXRhO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIHBvcnRmb2xpbyByZXBvc2l0b3J5IGV4aXN0cyBmb3IgYSB1c2VyXG4gICAqIE5vIGNvbnNlbnQgcmVxdWlyZWQgLSB0aGlzIGlzIGEgcmVhZC1vbmx5IG9wZXJhdGlvblxuICAgKiBTRUNVUklUWSBGSVg6IEFkZGVkIFVuaWNvZGUgbm9ybWFsaXphdGlvbiBmb3IgdXNlciBpbnB1dCAoRE1DUC1TRUMtMDA0KVxuICAgKi9cbiAgYXN5bmMgY2hlY2tQb3J0Zm9saW9FeGlzdHModXNlcm5hbWU6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIC8vIE1FRElVTSBGSVg6IE5vcm1hbGl6ZSB1c2VybmFtZSB0byBwcmV2ZW50IFVuaWNvZGUgYXR0YWNrc1xuICAgIGNvbnN0IG5vcm1hbGl6ZWRVc2VybmFtZSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHVzZXJuYW1lKS5ub3JtYWxpemVkQ29udGVudDtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVwbyA9IGF3YWl0IHRoaXMuZ2l0aHViUmVxdWVzdChcbiAgICAgICAgYC9yZXBvcy8ke25vcm1hbGl6ZWRVc2VybmFtZX0vJHtQb3J0Zm9saW9SZXBvTWFuYWdlci5QT1JURk9MSU9fUkVQT19OQU1FfWBcbiAgICAgICk7XG4gICAgICByZXR1cm4gcmVwbyAhPT0gbnVsbDtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gUmVwb3NpdG9yeSBkb2Vzbid0IGV4aXN0IG9yIEFQSSBlcnJvciAtIGJvdGggcmV0dXJuIGZhbHNlXG4gICAgICBFcnJvckhhbmRsZXIubG9nRXJyb3IoJ1BvcnRmb2xpb1JlcG9NYW5hZ2VyLmNoZWNrSWZSZXBvRXhpc3RzJywgZXJyb3IsIHsgdXNlcm5hbWUgfSk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBwb3J0Zm9saW8gcmVwb3NpdG9yeSB3aXRoIEVYUExJQ0lUIHVzZXIgY29uc2VudFxuICAgKiBAdGhyb3dzIEVycm9yIGlmIHVzZXIgZGVjbGluZXMgY29uc2VudCBvciBpZiBjb25zZW50IGlzIG5vdCBwcm92aWRlZFxuICAgKi9cbiAgYXN5bmMgY3JlYXRlUG9ydGZvbGlvKHVzZXJuYW1lOiBzdHJpbmcsIGNvbnNlbnQ6IGJvb2xlYW4gfCB1bmRlZmluZWQpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIC8vIE1FRElVTSBGSVg6IE5vcm1hbGl6ZSB1c2VybmFtZSB0byBwcmV2ZW50IFVuaWNvZGUgYXR0YWNrcyAoRE1DUC1TRUMtMDA0KVxuICAgIGNvbnN0IG5vcm1hbGl6ZWRVc2VybmFtZSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHVzZXJuYW1lKS5ub3JtYWxpemVkQ29udGVudDtcbiAgICBcbiAgICAvLyBDUklUSUNBTDogVmFsaWRhdGUgY29uc2VudCBpcyBleHBsaWNpdGx5IHByb3ZpZGVkXG4gICAgaWYgKGNvbnNlbnQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDb25zZW50IGlzIHJlcXVpcmVkIGZvciBwb3J0Zm9saW8gY3JlYXRpb24nKTtcbiAgICB9XG5cbiAgICBpZiAoIWNvbnNlbnQpIHtcbiAgICAgIGxvZ2dlci5pbmZvKGBVc2VyIGRlY2xpbmVkIHBvcnRmb2xpbyBjcmVhdGlvbiBmb3IgJHt1c2VybmFtZX1gKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVXNlciBkZWNsaW5lZCBwb3J0Zm9saW8gY3JlYXRpb24nKTtcbiAgICB9XG5cbiAgICAvLyBMb2cgY29uc2VudCBmb3IgYXVkaXQgdHJhaWxcbiAgICBsb2dnZXIuaW5mbyhgVXNlciBjb25zZW50ZWQgdG8gcG9ydGZvbGlvIGNyZWF0aW9uIGZvciAke25vcm1hbGl6ZWRVc2VybmFtZX1gKTtcbiAgICBcbiAgICAvLyBMT1cgRklYOiBBZGQgc2VjdXJpdHkgYXVkaXQgbG9nZ2luZyAoRE1DUC1TRUMtMDA2KVxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdQT1JURk9MSU9fSU5JVElBTElaQVRJT04nLFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnUG9ydGZvbGlvUmVwb01hbmFnZXIuY3JlYXRlUG9ydGZvbGlvJyxcbiAgICAgIGRldGFpbHM6IGBVc2VyICR7bm9ybWFsaXplZFVzZXJuYW1lfSBjb25zZW50ZWQgdG8gcG9ydGZvbGlvIGNyZWF0aW9uYCxcbiAgICAgIG1ldGFkYXRhOiB7IHVzZXJuYW1lOiBub3JtYWxpemVkVXNlcm5hbWUgfVxuICAgIH0pO1xuXG4gICAgLy8gQ2hlY2sgaWYgcG9ydGZvbGlvIGFscmVhZHkgZXhpc3RzXG4gICAgY29uc3QgZXhpc3RpbmdSZXBvID0gYXdhaXQgdGhpcy5naXRodWJSZXF1ZXN0KFxuICAgICAgYC9yZXBvcy8ke25vcm1hbGl6ZWRVc2VybmFtZX0vJHtQb3J0Zm9saW9SZXBvTWFuYWdlci5QT1JURk9MSU9fUkVQT19OQU1FfWBcbiAgICApO1xuICAgIFxuICAgIGlmIChleGlzdGluZ1JlcG8gJiYgZXhpc3RpbmdSZXBvLmh0bWxfdXJsKSB7XG4gICAgICBsb2dnZXIuaW5mbyhgUG9ydGZvbGlvIGFscmVhZHkgZXhpc3RzIGZvciAke25vcm1hbGl6ZWRVc2VybmFtZX1gKTtcbiAgICAgIHJldHVybiBleGlzdGluZ1JlcG8uaHRtbF91cmw7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIHRoZSBwb3J0Zm9saW8gcmVwb3NpdG9yeVxuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXBvID0gYXdhaXQgdGhpcy5naXRodWJSZXF1ZXN0KFxuICAgICAgICAnL3VzZXIvcmVwb3MnLFxuICAgICAgICAnUE9TVCcsXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiBQb3J0Zm9saW9SZXBvTWFuYWdlci5QT1JURk9MSU9fUkVQT19OQU1FLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBQb3J0Zm9saW9SZXBvTWFuYWdlci5ERUZBVUxUX0RFU0NSSVBUSU9OLFxuICAgICAgICAgIHByaXZhdGU6IGZhbHNlLFxuICAgICAgICAgIGF1dG9faW5pdDogdHJ1ZVxuICAgICAgICB9XG4gICAgICApO1xuXG4gICAgICAvLyBJbml0aWFsaXplIHBvcnRmb2xpbyBzdHJ1Y3R1cmVcbiAgICAgIGF3YWl0IHRoaXMuZ2VuZXJhdGVQb3J0Zm9saW9TdHJ1Y3R1cmUobm9ybWFsaXplZFVzZXJuYW1lKTtcblxuICAgICAgcmV0dXJuIHJlcG8uaHRtbF91cmw7XG4gICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgLy8gSGFuZGxlIHJhY2UgY29uZGl0aW9uOiBpZiByZXBvc2l0b3J5IHdhcyBjcmVhdGVkIGJldHdlZW4gb3VyIGNoZWNrIGFuZCBjcmVhdGlvbiBhdHRlbXB0XG4gICAgICBpZiAoZXJyb3IubWVzc2FnZSAmJiBlcnJvci5tZXNzYWdlLmluY2x1ZGVzKCduYW1lIGFscmVhZHkgZXhpc3RzJykpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oYFBvcnRmb2xpbyByZXBvc2l0b3J5IGFscmVhZHkgZXhpc3RzIGZvciAke25vcm1hbGl6ZWRVc2VybmFtZX0gKHJhY2UgY29uZGl0aW9uIGhhbmRsZWQpYCk7XG4gICAgICAgIFxuICAgICAgICAvLyBSZS1jaGVjayBmb3IgdGhlIGV4aXN0aW5nIHJlcG9zaXRvcnkgYW5kIHJldHVybiBpdHMgVVJMXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZXhpc3RpbmdSZXBvID0gYXdhaXQgdGhpcy5naXRodWJSZXF1ZXN0KFxuICAgICAgICAgICAgYC9yZXBvcy8ke25vcm1hbGl6ZWRVc2VybmFtZX0vJHtQb3J0Zm9saW9SZXBvTWFuYWdlci5QT1JURk9MSU9fUkVQT19OQU1FfWBcbiAgICAgICAgICApO1xuICAgICAgICAgIGlmIChleGlzdGluZ1JlcG8gJiYgZXhpc3RpbmdSZXBvLmh0bWxfdXJsKSB7XG4gICAgICAgICAgICByZXR1cm4gZXhpc3RpbmdSZXBvLmh0bWxfdXJsO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAocmVjaGVja0Vycm9yKSB7XG4gICAgICAgICAgRXJyb3JIYW5kbGVyLmxvZ0Vycm9yKCdQb3J0Zm9saW9SZXBvTWFuYWdlci5yZWNoZWNrRXhpc3RpbmdSZXBvJywgcmVjaGVja0Vycm9yLCB7IHVzZXJuYW1lOiBub3JtYWxpemVkVXNlcm5hbWUgfSk7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIElmIHdlIGNhbid0IGdldCB0aGUgZXhpc3RpbmcgcmVwbywgdGhyb3cgYSBtb3JlIHNwZWNpZmljIGVycm9yXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgUG9ydGZvbGlvIHJlcG9zaXRvcnkgYWxyZWFkeSBleGlzdHMgZm9yICR7bm9ybWFsaXplZFVzZXJuYW1lfS4gUGxlYXNlIGNoZWNrIHlvdXIgR2l0SHViIGFjY291bnQuYCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIEVycm9ySGFuZGxlci5sb2dFcnJvcignUG9ydGZvbGlvUmVwb01hbmFnZXIuY3JlYXRlUG9ydGZvbGlvUmVwbycsIGVycm9yLCB7IHVzZXJuYW1lOiBub3JtYWxpemVkVXNlcm5hbWUgfSk7XG4gICAgICB0aHJvdyBFcnJvckhhbmRsZXIud3JhcEVycm9yKGVycm9yLCBgRmFpbGVkIHRvIGNyZWF0ZSBwb3J0Zm9saW8gcmVwb3NpdG9yeSBmb3IgJHtub3JtYWxpemVkVXNlcm5hbWV9LiAke2Vycm9yLm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3Igb2NjdXJyZWQuJ31gLCBFcnJvckNhdGVnb3J5Lk5FVFdPUktfRVJST1IpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTYXZlIGVsZW1lbnQgdG8gcG9ydGZvbGlvIHdpdGggRVhQTElDSVQgdXNlciBjb25zZW50XG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgdXNlciBkZWNsaW5lcyBjb25zZW50IG9yIGVsZW1lbnQgaXMgaW52YWxpZFxuICAgKi9cbiAgYXN5bmMgc2F2ZUVsZW1lbnQoZWxlbWVudDogSUVsZW1lbnQsIGNvbnNlbnQ6IGJvb2xlYW4gfCB1bmRlZmluZWQpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIC8vIENSSVRJQ0FMOiBWYWxpZGF0ZSBjb25zZW50IGlzIGV4cGxpY2l0bHkgcHJvdmlkZWRcbiAgICBpZiAoY29uc2VudCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvbnNlbnQgaXMgcmVxdWlyZWQgdG8gc2F2ZSBlbGVtZW50Jyk7XG4gICAgfVxuXG4gICAgaWYgKCFjb25zZW50KSB7XG4gICAgICBsb2dnZXIuaW5mbyhgVXNlciBkZWNsaW5lZCB0byBzYXZlIGVsZW1lbnQgJHtlbGVtZW50LmlkfSB0byBwb3J0Zm9saW9gKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVXNlciBkZWNsaW5lZCB0byBzYXZlIGVsZW1lbnQgdG8gcG9ydGZvbGlvJyk7XG4gICAgfVxuXG4gICAgLy8gVmFsaWRhdGUgZWxlbWVudCBiZWZvcmUgc2F2aW5nXG4gICAgdGhpcy52YWxpZGF0ZUVsZW1lbnQoZWxlbWVudCk7XG5cbiAgICAvLyBNRURJVU0gRklYOiBOb3JtYWxpemUgdXNlcm5hbWUgZnJvbSBlbGVtZW50IG1ldGFkYXRhIChETUNQLVNFQy0wMDQpXG4gICAgY29uc3QgcmF3VXNlcm5hbWUgPSBlbGVtZW50Lm1ldGFkYXRhLmF1dGhvciB8fCAnYW5vbnltb3VzJztcbiAgICBjb25zdCB1c2VybmFtZSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHJhd1VzZXJuYW1lKS5ub3JtYWxpemVkQ29udGVudDtcbiAgICBsb2dnZXIuaW5mbyhgVXNlciBjb25zZW50ZWQgdG8gc2F2ZSBlbGVtZW50ICR7ZWxlbWVudC5pZH0gdG8gcG9ydGZvbGlvYCk7XG4gICAgXG4gICAgLy8gTE9XIEZJWDogQWRkIHNlY3VyaXR5IGF1ZGl0IGxvZ2dpbmcgZm9yIGVsZW1lbnQgc2F2ZSAoRE1DUC1TRUMtMDA2KVxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdFTEVNRU5UX0NSRUFURUQnLFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnUG9ydGZvbGlvUmVwb01hbmFnZXIuc2F2ZUVsZW1lbnQnLFxuICAgICAgZGV0YWlsczogYFVzZXIgY29uc2VudGVkIHRvIHNhdmUgZWxlbWVudCAke2VsZW1lbnQuaWR9IHRvIHBvcnRmb2xpb2AsXG4gICAgICBtZXRhZGF0YTogeyBcbiAgICAgICAgZWxlbWVudElkOiBlbGVtZW50LmlkLFxuICAgICAgICBlbGVtZW50VHlwZTogZWxlbWVudC50eXBlLFxuICAgICAgICB1c2VybmFtZSBcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIC8vIEdlbmVyYXRlIGZpbGUgcGF0aCBiYXNlZCBvbiBlbGVtZW50IHR5cGVcbiAgICAvLyBGSVg6IERvbid0IGFkZCAncycgLSBlbGVtZW50LnR5cGUgaXMgYWxyZWFkeSBwbHVyYWwgKGUuZy4sICdwZXJzb25hcycsICdza2lsbHMnKVxuICAgIGNvbnN0IGZpbGVOYW1lID0gdGhpcy5nZW5lcmF0ZUZpbGVOYW1lKGVsZW1lbnQubWV0YWRhdGEubmFtZSk7XG4gICAgY29uc3QgZmlsZVBhdGggPSBgJHtlbGVtZW50LnR5cGV9LyR7ZmlsZU5hbWV9Lm1kYDtcblxuICAgIC8vIFByZXBhcmUgY29udGVudCAoY291bGQgYmUgbWFya2Rvd24gd2l0aCBmcm9udG1hdHRlcilcbiAgICBjb25zdCBjb250ZW50ID0gdGhpcy5mb3JtYXRFbGVtZW50Q29udGVudChlbGVtZW50KTtcblxuICAgIC8vIFNhdmUgdG8gR2l0SHViXG4gICAgdHJ5IHtcbiAgICAgIC8vIEZpcnN0LCBjaGVjayBpZiBmaWxlIGV4aXN0cyB0byBkZXRlcm1pbmUgaWYgdGhpcyBpcyBjcmVhdGUgb3IgdXBkYXRlXG4gICAgICBjb25zdCBleGlzdGluZ0ZpbGUgPSBhd2FpdCB0aGlzLmdpdGh1YlJlcXVlc3QoXG4gICAgICAgIGAvcmVwb3MvJHt1c2VybmFtZX0vJHtQb3J0Zm9saW9SZXBvTWFuYWdlci5QT1JURk9MSU9fUkVQT19OQU1FfS9jb250ZW50cy8ke2ZpbGVQYXRofWBcbiAgICAgICk7XG5cbiAgICAgIC8vIENyZWF0ZSBvciB1cGRhdGUgdGhlIGZpbGVcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuZ2l0aHViUmVxdWVzdChcbiAgICAgICAgYC9yZXBvcy8ke3VzZXJuYW1lfS8ke1BvcnRmb2xpb1JlcG9NYW5hZ2VyLlBPUlRGT0xJT19SRVBPX05BTUV9L2NvbnRlbnRzLyR7ZmlsZVBhdGh9YCxcbiAgICAgICAgJ1BVVCcsXG4gICAgICAgIHtcbiAgICAgICAgICBtZXNzYWdlOiBgQWRkICR7ZWxlbWVudC5tZXRhZGF0YS5uYW1lfSB0byBwb3J0Zm9saW9gLFxuICAgICAgICAgIGNvbnRlbnQ6IEJ1ZmZlci5mcm9tKGNvbnRlbnQpLnRvU3RyaW5nKCdiYXNlNjQnKSxcbiAgICAgICAgICBzaGE6IGV4aXN0aW5nRmlsZT8uc2hhIC8vIEluY2x1ZGUgU0hBIGlmIHVwZGF0aW5nIGV4aXN0aW5nIGZpbGVcbiAgICAgICAgfVxuICAgICAgKTtcblxuICAgICAgcmV0dXJuIHJlc3VsdC5jb21taXQuaHRtbF91cmw7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIEVycm9ySGFuZGxlci5sb2dFcnJvcignUG9ydGZvbGlvUmVwb01hbmFnZXIuc2F2ZUVsZW1lbnRUb1JlcG8nLCBlcnJvciwgeyBcbiAgICAgICAgZWxlbWVudElkOiBlbGVtZW50LmlkLFxuICAgICAgICB1c2VybmFtZSBcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgRXJyb3JIYW5kbGVyLndyYXBFcnJvcihlcnJvciwgJ0ZhaWxlZCB0byBzYXZlIGVsZW1lbnQgdG8gcG9ydGZvbGlvJywgRXJyb3JDYXRlZ29yeS5ORVRXT1JLX0VSUk9SKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgaW5pdGlhbCBwb3J0Zm9saW8gc3RydWN0dXJlIHdpdGggUkVBRE1FIGFuZCBkaXJlY3Rvcmllc1xuICAgKiBTRUNVUklUWTogVXNlcm5hbWUgYWxyZWFkeSBub3JtYWxpemVkIGJ5IGNhbGxpbmcgbWV0aG9kc1xuICAgKi9cbiAgYXN5bmMgZ2VuZXJhdGVQb3J0Zm9saW9TdHJ1Y3R1cmUodXNlcm5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIENyZWF0ZSBSRUFETUUubWRcbiAgICBjb25zdCByZWFkbWVDb250ZW50ID0gYCMgRG9sbGhvdXNlTUNQIFBvcnRmb2xpb1xuXG5UaGlzIGlzIG15IHBlcnNvbmFsIGNvbGxlY3Rpb24gb2YgRG9sbGhvdXNlTUNQIGVsZW1lbnRzLlxuXG4jIyBTdHJ1Y3R1cmVcblxuLSAqKnBlcnNvbmFzLyoqIC0gQmVoYXZpb3JhbCBwcm9maWxlc1xuLSAqKnNraWxscy8qKiAtIERpc2NyZXRlIGNhcGFiaWxpdGllcyAgXG4tICoqdGVtcGxhdGVzLyoqIC0gUmV1c2FibGUgY29udGVudCBzdHJ1Y3R1cmVzXG4tICoqYWdlbnRzLyoqIC0gQXV0b25vbW91cyBhY3RvcnNcbi0gKiptZW1vcmllcy8qKiAtIFBlcnNpc3RlbnQgY29udGV4dFxuLSAqKmVuc2VtYmxlcy8qKiAtIEVsZW1lbnQgZ3JvdXBzXG5cbiMjIFVzYWdlXG5cblRoZXNlIGVsZW1lbnRzIGNhbiBiZSBpbXBvcnRlZCBpbnRvIHlvdXIgRG9sbGhvdXNlTUNQIGluc3RhbGxhdGlvbi5cblxuLS0tXG4qR2VuZXJhdGVkIGJ5IERvbGxob3VzZU1DUCpcbmA7XG5cbiAgICBhd2FpdCB0aGlzLmdpdGh1YlJlcXVlc3QoXG4gICAgICBgL3JlcG9zLyR7dXNlcm5hbWV9LyR7UG9ydGZvbGlvUmVwb01hbmFnZXIuUE9SVEZPTElPX1JFUE9fTkFNRX0vY29udGVudHMvUkVBRE1FLm1kYCxcbiAgICAgICdQVVQnLFxuICAgICAge1xuICAgICAgICBtZXNzYWdlOiAnSW5pdGlhbGl6ZSBwb3J0Zm9saW8gc3RydWN0dXJlJyxcbiAgICAgICAgY29udGVudDogQnVmZmVyLmZyb20ocmVhZG1lQ29udGVudCkudG9TdHJpbmcoJ2Jhc2U2NCcpXG4gICAgICB9XG4gICAgKTtcblxuICAgIC8vIENyZWF0ZSBkaXJlY3RvcnkgcGxhY2Vob2xkZXJzXG4gICAgY29uc3QgZGlyZWN0b3JpZXMgPSBbJ3BlcnNvbmFzJywgJ3NraWxscycsICd0ZW1wbGF0ZXMnLCAnYWdlbnRzJywgJ21lbW9yaWVzJywgJ2Vuc2VtYmxlcyddO1xuICAgIFxuICAgIGZvciAoY29uc3QgZGlyIG9mIGRpcmVjdG9yaWVzKSB7XG4gICAgICBhd2FpdCB0aGlzLmdpdGh1YlJlcXVlc3QoXG4gICAgICAgIGAvcmVwb3MvJHt1c2VybmFtZX0vJHtQb3J0Zm9saW9SZXBvTWFuYWdlci5QT1JURk9MSU9fUkVQT19OQU1FfS9jb250ZW50cy8ke2Rpcn0vLmdpdGtlZXBgLFxuICAgICAgICAnUFVUJyxcbiAgICAgICAge1xuICAgICAgICAgIG1lc3NhZ2U6IGBDcmVhdGUgJHtkaXJ9IGRpcmVjdG9yeWAsXG4gICAgICAgICAgY29udGVudDogQnVmZmVyLmZyb20oJycpLnRvU3RyaW5nKCdiYXNlNjQnKVxuICAgICAgICB9XG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBlbGVtZW50IGJlZm9yZSBzYXZpbmdcbiAgICogQHRocm93cyBFcnJvciBpZiBlbGVtZW50IGlzIGludmFsaWRcbiAgICovXG4gIHByaXZhdGUgdmFsaWRhdGVFbGVtZW50KGVsZW1lbnQ6IElFbGVtZW50KTogdm9pZCB7XG4gICAgaWYgKCFlbGVtZW50Lm1ldGFkYXRhLm5hbWUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBlbGVtZW50OiBuYW1lIGlzIHJlcXVpcmVkJyk7XG4gICAgfVxuXG4gICAgaWYgKCFlbGVtZW50LmlkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgZWxlbWVudDogaWQgaXMgcmVxdWlyZWQnKTtcbiAgICB9XG5cbiAgICBpZiAoIWVsZW1lbnQudHlwZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGVsZW1lbnQ6IHR5cGUgaXMgcmVxdWlyZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgc2FmZSBmaWxlbmFtZSBmcm9tIGVsZW1lbnQgbmFtZVxuICAgKiBTRUNVUklUWTogQWRkaXRpb25hbCBVbmljb2RlIG5vcm1hbGl6YXRpb24gZm9yIGZpbGVuYW1lc1xuICAgKiBTRUNVUklUWSBGSVg6IEZpeGVkIFJlRG9TIHZ1bG5lcmFiaWxpdHkgd2l0aCBpbnB1dCBsZW5ndGggbGltaXQgYW5kIG9wdGltaXplZCByZWdleFxuICAgKi9cbiAgcHJpdmF0ZSBnZW5lcmF0ZUZpbGVOYW1lKG5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgLy8gU0VDVVJJVFkgRklYOiBMaW1pdCBpbnB1dCBsZW5ndGggdG8gcHJldmVudCBSZURvUyBhdHRhY2tzXG4gICAgLy8gRXZlbiB3aXRoIG9wdGltaXplZCByZWdleCwgdmVyeSBsb25nIGlucHV0cyBjb3VsZCBjYXVzZSBwZXJmb3JtYW5jZSBpc3N1ZXNcbiAgICBjb25zdCBNQVhfRklMRU5BTUVfTEVOR1RIID0gMjU1OyAvLyBDb21tb24gZmlsZXN5c3RlbSBsaW1pdFxuICAgIFxuICAgIC8vIE5vcm1hbGl6ZSB0byBwcmV2ZW50IFVuaWNvZGUgYXR0YWNrcyBpbiBmaWxlbmFtZXNcbiAgICBjb25zdCBub3JtYWxpemVkTmFtZSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKG5hbWUpLm5vcm1hbGl6ZWRDb250ZW50O1xuICAgIFxuICAgIC8vIFRydW5jYXRlIHRvIHNhZmUgbGVuZ3RoIEJFRk9SRSByZWdleCBvcGVyYXRpb25zXG4gICAgY29uc3QgdHJ1bmNhdGVkTmFtZSA9IG5vcm1hbGl6ZWROYW1lLnNsaWNlKDAsIE1BWF9GSUxFTkFNRV9MRU5HVEgpO1xuICAgIFxuICAgIC8vIFNFQ1VSSVRZIEZJWDogT3B0aW1pemVkIHJlZ2V4IG9wZXJhdGlvbnMgdG8gcHJldmVudCBSZURvU1xuICAgIC8vIDEuIENvbnZlcnQgbm9uLWFscGhhbnVtZXJpYyBzZXF1ZW5jZXMgdG8gc2luZ2xlIGRhc2hcbiAgICAvLyAyLiBSZW1vdmUgbGVhZGluZy90cmFpbGluZyBkYXNoZXMgaW4gYSBzaW5nbGUgcGFzcyB1c2luZyB0cmltXG4gICAgY29uc3Qgc2FmZU5hbWUgPSB0cnVuY2F0ZWROYW1lXG4gICAgICAudG9Mb3dlckNhc2UoKVxuICAgICAgLnJlcGxhY2UoL1teYS16MC05XSsvZywgJy0nKVxuICAgICAgLnJlcGxhY2UoL14tKy8sICcnKSAgLy8gUmVtb3ZlIGxlYWRpbmcgZGFzaGVzXG4gICAgICAucmVwbGFjZSgvLSskLywgJycpOyAvLyBSZW1vdmUgdHJhaWxpbmcgZGFzaGVzXG4gICAgXG4gICAgLy8gRW5zdXJlIHdlIGhhdmUgYSB2YWxpZCBmaWxlbmFtZSAobm90IGVtcHR5IGFmdGVyIGNsZWFuaW5nKVxuICAgIHJldHVybiBzYWZlTmFtZSB8fCAndW5uYW1lZCc7XG4gIH1cblxuICAvKipcbiAgICogRm9ybWF0IGVsZW1lbnQgY29udGVudCBmb3Igc3RvcmFnZVxuICAgKi9cbiAgcHJpdmF0ZSBmb3JtYXRFbGVtZW50Q29udGVudChlbGVtZW50OiBJRWxlbWVudCk6IHN0cmluZyB7XG4gICAgLy8gU2VyaWFsaXplIHRoZSBlbGVtZW50IG9yIGNyZWF0ZSBiYXNpYyBtYXJrZG93blxuICAgIGlmIChlbGVtZW50LnNlcmlhbGl6ZSkge1xuICAgICAgcmV0dXJuIGVsZW1lbnQuc2VyaWFsaXplKCk7XG4gICAgfVxuICAgIC8vIEZhbGxiYWNrIHRvIGJhc2ljIG1hcmtkb3duIGZvcm1hdFxuICAgIHJldHVybiBgIyAke2VsZW1lbnQubWV0YWRhdGEubmFtZX1cXG5cXG4ke2VsZW1lbnQubWV0YWRhdGEuZGVzY3JpcHRpb24gfHwgJyd9YDtcbiAgfVxufSJdfQ==
|