@mickdarling/dollhousemcp
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
213 lines • 26 kB
JavaScript
/**
* SignatureVerifier - Verifies GitHub release signatures to ensure authenticity
*
* Security features:
* - Verifies GPG signatures on git tags
* - Validates release artifacts checksums
* - Ensures releases come from trusted sources
* - Prevents tampering and supply chain attacks
*/
import { safeExec } from '../utils/git.js';
import * as crypto from 'crypto';
import * as fs from 'fs/promises';
import * as path from 'path';
import { randomBytes } from 'crypto';
export class SignatureVerifier {
trustedKeys;
allowUnsignedInDev;
constructor(options) {
// Default trusted keys - should be GPG key IDs of maintainers
this.trustedKeys = new Set(options?.trustedKeys || [
// Add trusted GPG key fingerprints here
// Example: '1234567890ABCDEF1234567890ABCDEF12345678'
]);
// Allow unsigned releases in development mode
this.allowUnsignedInDev = options?.allowUnsignedInDev ?? true;
}
/**
* Verify a git tag signature
* @param tagName The tag to verify (e.g., 'v1.2.0')
* @returns Verification result with signer information
*/
async verifyTagSignature(tagName) {
try {
// Check if GPG is available
try {
await safeExec('gpg', ['--version']);
}
catch {
return {
verified: false,
error: 'GPG is not installed or not available in PATH'
};
}
// Verify the tag signature
const { stdout, stderr } = await safeExec('git', ['verify-tag', tagName]);
// Parse GPG output (comes on stderr)
const output = stderr || stdout;
// Check for good signature
if (!output.includes('Good signature')) {
// Check if tag is unsigned
if (output.includes('error: no signature found')) {
if (this.allowUnsignedInDev && process.env.NODE_ENV !== 'production') {
return {
verified: true,
error: 'Tag is unsigned (allowed in development mode)'
};
}
return {
verified: false,
error: 'Tag is not signed'
};
}
return {
verified: false,
error: 'Invalid signature'
};
}
// Extract signer information
const keyMatch = output.match(/key (?:ID )?([A-F0-9]+)/i);
const emailMatch = output.match(/"([^"]+)"/);
const dateMatch = output.match(/made (\w+ \w+ \d+ \d+:\d+:\d+ \d+ \w+)/);
const signerKey = keyMatch ? keyMatch[1] : undefined;
const signerEmail = emailMatch ? emailMatch[1] : undefined;
const signatureDate = dateMatch ? new Date(dateMatch[1]) : undefined;
// Check if key is trusted
if (this.trustedKeys.size > 0 && signerKey) {
// Check if the key ID ends with any of our trusted keys
const isTrusted = Array.from(this.trustedKeys).some(trustedKey => signerKey.endsWith(trustedKey.toUpperCase()));
if (!isTrusted) {
return {
verified: false,
signerKey,
signerEmail,
signatureDate,
error: `Signature is valid but key ${signerKey} is not in trusted keys list`
};
}
}
return {
verified: true,
signerKey,
signerEmail,
signatureDate
};
}
catch (error) {
return {
verified: false,
error: `Failed to verify signature: ${error instanceof Error ? error.message : String(error)}`
};
}
}
/**
* Verify a file checksum against expected value
* @param filePath Path to the file to verify
* @param expectedChecksum Expected SHA256 checksum
* @returns Verification result
*/
async verifyChecksum(filePath, expectedChecksum) {
try {
// Read file and calculate checksum
const fileBuffer = await fs.readFile(filePath);
const hash = crypto.createHash('sha256');
hash.update(fileBuffer);
const actualChecksum = hash.digest('hex');
// Compare checksums
const verified = actualChecksum.toLowerCase() === expectedChecksum.toLowerCase();
return {
verified,
expectedChecksum,
actualChecksum,
error: verified ? undefined : 'Checksum mismatch'
};
}
catch (error) {
return {
verified: false,
expectedChecksum,
error: `Failed to verify checksum: ${error instanceof Error ? error.message : String(error)}`
};
}
}
/**
* Verify release artifacts using a checksums file
* @param checksumsFile Path to checksums file (e.g., SHA256SUMS)
* @param artifactDir Directory containing artifacts to verify
* @returns Map of filename to verification result
*/
async verifyReleaseArtifacts(checksumsFile, artifactDir) {
const results = new Map();
try {
// Read checksums file
const checksumsContent = await fs.readFile(checksumsFile, 'utf-8');
const lines = checksumsContent.split('\n').filter(line => line.trim());
// Parse checksums (format: "checksum filename" or "checksum *filename")
for (const line of lines) {
const match = line.match(/^([a-f0-9]+)\s+\*?(.+)$/i);
if (!match)
continue;
const [, checksum, filename] = match;
const filePath = path.join(artifactDir, filename);
// Verify each file
const result = await this.verifyChecksum(filePath, checksum);
results.set(filename, result);
}
return results;
}
catch (error) {
// If we can't read the checksums file, mark all as unverified
results.set('*', {
verified: false,
error: `Failed to read checksums file: ${error instanceof Error ? error.message : String(error)}`
});
return results;
}
}
/**
* Add a trusted key for signature verification
* @param keyId GPG key ID or fingerprint
*/
addTrustedKey(keyId) {
this.trustedKeys.add(keyId.toUpperCase());
}
/**
* Remove a trusted key
* @param keyId GPG key ID or fingerprint
*/
removeTrustedKey(keyId) {
this.trustedKeys.delete(keyId.toUpperCase());
}
/**
* Get list of trusted keys
*/
getTrustedKeys() {
return Array.from(this.trustedKeys);
}
/**
* Import a GPG public key
* @param keyData The public key data to import
* @returns Success status
*/
async importPublicKey(keyData) {
try {
// Write key data to temporary file with secure random name
const tempFile = path.join(process.cwd(), `.gpg-import-${randomBytes(8).toString('hex')}.asc`);
await fs.writeFile(tempFile, keyData);
try {
// Import the key
await safeExec('gpg', ['--import', tempFile]);
return true;
}
finally {
// Clean up temp file
await fs.unlink(tempFile).catch(() => { });
}
}
catch (error) {
console.error('Failed to import GPG key:', error);
return false;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2lnbmF0dXJlVmVyaWZpZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdXBkYXRlL1NpZ25hdHVyZVZlcmlmaWVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7OztHQVFHO0FBRUgsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzNDLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFpQnJDLE1BQU0sT0FBTyxpQkFBaUI7SUFDcEIsV0FBVyxDQUFjO0lBQ3pCLGtCQUFrQixDQUFVO0lBRXBDLFlBQVksT0FHWDtRQUNDLDhEQUE4RDtRQUM5RCxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksR0FBRyxDQUFDLE9BQU8sRUFBRSxXQUFXLElBQUk7UUFDakQsd0NBQXdDO1FBQ3hDLHNEQUFzRDtTQUN2RCxDQUFDLENBQUM7UUFFSCw4Q0FBOEM7UUFDOUMsSUFBSSxDQUFDLGtCQUFrQixHQUFHLE9BQU8sRUFBRSxrQkFBa0IsSUFBSSxJQUFJLENBQUM7SUFDaEUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxLQUFLLENBQUMsa0JBQWtCLENBQUMsT0FBZTtRQUN0QyxJQUFJLENBQUM7WUFDSCw0QkFBNEI7WUFDNUIsSUFBSSxDQUFDO2dCQUNILE1BQU0sUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7WUFDdkMsQ0FBQztZQUFDLE1BQU0sQ0FBQztnQkFDUCxPQUFPO29CQUNMLFFBQVEsRUFBRSxLQUFLO29CQUNmLEtBQUssRUFBRSwrQ0FBK0M7aUJBQ3ZELENBQUM7WUFDSixDQUFDO1lBRUQsMkJBQTJCO1lBQzNCLE1BQU0sRUFBRSxNQUFNLEVB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dWIgcmVsZWFzZSBzaWduYXR1cmVzIHRvIGVuc3VyZSBhdXRoZW50aWNpdHlcbiAqIFxuICogU2VjdXJpdHkgZmVhdHVyZXM6XG4gKiAtIFZlcmlmaWVzIEdQRyBzaWduYXR1cmVzIG9uIGdpdCB0YWdzXG4gKiAtIFZhbGlkYXRlcyByZWxlYXNlIGFydGlmYWN0cyBjaGVja3N1bXNcbiAqIC0gRW5zdXJlcyByZWxlYXNlcyBjb21lIGZyb20gdHJ1c3RlZCBzb3VyY2VzXG4gKiAtIFByZXZlbnRzIHRhbXBlcmluZyBhbmQgc3VwcGx5IGNoYWluIGF0dGFja3NcbiAqL1xuXG5pbXBvcnQgeyBzYWZlRXhlYyB9IGZyb20gJy4uL3V0aWxzL2dpdC5qcyc7XG5pbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSAnY3J5cHRvJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyByYW5kb21CeXRlcyB9IGZyb20gJ2NyeXB0byc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2lnbmF0dXJlVmVyaWZpY2F0aW9uUmVzdWx0IHtcbiAgdmVyaWZpZWQ6IGJvb2xlYW47XG4gIHNpZ25lcktleT86IHN0cmluZztcbiAgc2lnbmVyRW1haWw/OiBzdHJpbmc7XG4gIHNpZ25hdHVyZURhdGU/OiBEYXRlO1xuICBlcnJvcj86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDaGVja3N1bVZlcmlmaWNhdGlvblJlc3VsdCB7XG4gIHZlcmlmaWVkOiBib29sZWFuO1xuICBleHBlY3RlZENoZWNrc3VtPzogc3RyaW5nO1xuICBhY3R1YWxDaGVja3N1bT86IHN0cmluZztcbiAgZXJyb3I/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBjbGFzcyBTaWduYXR1cmVWZXJpZmllciB7XG4gIHByaXZhdGUgdHJ1c3RlZEtleXM6IFNldDxzdHJpbmc+O1xuICBwcml2YXRlIGFsbG93VW5zaWduZWRJbkRldjogYm9vbGVhbjtcbiAgXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM/OiB7XG4gICAgdHJ1c3RlZEtleXM/OiBzdHJpbmdbXTtcbiAgICBhbGxvd1Vuc2lnbmVkSW5EZXY/OiBib29sZWFuO1xuICB9KSB7XG4gICAgLy8gRGVmYXVsdCB0cnVzdGVkIGtleXMgLSBzaG91bGQgYmUgR1BHIGtleSBJRHMgb2YgbWFpbnRhaW5lcnNcbiAgICB0aGlzLnRydXN0ZWRLZXlzID0gbmV3IFNldChvcHRpb25zPy50cnVzdGVkS2V5cyB8fCBbXG4gICAgICAvLyBBZGQgdHJ1c3RlZCBHUEcga2V5IGZpbmdlcnByaW50cyBoZXJlXG4gICAgICAvLyBFeGFtcGxlOiAnMTIzNDU2Nzg5MEFCQ0RFRjEyMzQ1Njc4OTBBQkNERUYxMjM0NTY3OCdcbiAgICBdKTtcbiAgICBcbiAgICAvLyBBbGxvdyB1bnNpZ25lZCByZWxlYXNlcyBpbiBkZXZlbG9wbWVudCBtb2RlXG4gICAgdGhpcy5hbGxvd1Vuc2lnbmVkSW5EZXYgPSBvcHRpb25zPy5hbGxvd1Vuc2lnbmVkSW5EZXYgPz8gdHJ1ZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFZlcmlmeSBhIGdpdCB0YWcgc2lnbmF0dXJlXG4gICAqIEBwYXJhbSB0YWdOYW1lIFRoZSB0YWcgdG8gdmVyaWZ5IChlLmcuLCAndjEuMi4wJylcbiAgICogQHJldHVybnMgVmVyaWZpY2F0aW9uIHJlc3VsdCB3aXRoIHNpZ25lciBpbmZvcm1hdGlvblxuICAgKi9cbiAgYXN5bmMgdmVyaWZ5VGFnU2lnbmF0dXJlKHRhZ05hbWU6IHN0cmluZyk6IFByb21pc2U8U2lnbmF0dXJlVmVyaWZpY2F0aW9uUmVzdWx0PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIENoZWNrIGlmIEdQRyBpcyBhdmFpbGFibGVcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHNhZmVFeGVjKCdncGcnLCBbJy0tdmVyc2lvbiddKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHZlcmlmaWVkOiBmYWxzZSxcbiAgICAgICAgICBlcnJvcjogJ0dQRyBpcyBub3QgaW5zdGFsbGVkIG9yIG5vdCBhdmFpbGFibGUgaW4gUEFUSCdcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gVmVyaWZ5IHRoZSB0YWcgc2lnbmF0dXJlXG4gICAgICBjb25zdCB7IHN0ZG91dCwgc3RkZXJyIH0gPSBhd2FpdCBzYWZlRXhlYygnZ2l0JywgWyd2ZXJpZnktdGFnJywgdGFnTmFtZV0pO1xuICAgICAgXG4gICAgICAvLyBQYXJzZSBHUEcgb3V0cHV0IChjb21lcyBvbiBzdGRlcnIpXG4gICAgICBjb25zdCBvdXRwdXQgPSBzdGRlcnIgfHwgc3Rkb3V0O1xuICAgICAgXG4gICAgICAvLyBDaGVjayBmb3IgZ29vZCBzaWduYXR1cmVcbiAgICAgIGlmICghb3V0cHV0LmluY2x1ZGVzKCdHb29kIHNpZ25hdHVyZScpKSB7XG4gICAgICAgIC8vIENoZWNrIGlmIHRhZyBpcyB1bnNpZ25lZFxuICAgICAgICBpZiAob3V0cHV0LmluY2x1ZGVzKCdlcnJvcjogbm8gc2lnbmF0dXJlIGZvdW5kJykpIHtcbiAgICAgICAgICBpZiAodGhpcy5hbGxvd1Vuc2lnbmVkSW5EZXYgJiYgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgIT09ICdwcm9kdWN0aW9uJykge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgdmVyaWZpZWQ6IHRydWUsXG4gICAgICAgICAgICAgIGVycm9yOiAnVGFnIGlzIHVuc2lnbmVkIChhbGxvd2VkIGluIGRldmVsb3BtZW50IG1vZGUpJ1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHZlcmlmaWVkOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiAnVGFnIGlzIG5vdCBzaWduZWQnXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB2ZXJpZmllZDogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6ICdJbnZhbGlkIHNpZ25hdHVyZSdcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gRXh0cmFjdCBzaWduZXIgaW5mb3JtYXRpb25cbiAgICAgIGNvbnN0IGtleU1hdGNoID0gb3V0cHV0Lm1hdGNoKC9rZXkgKD86SUQgKT8oW0EtRjAtOV0rKS9pKTtcbiAgICAgIGNvbnN0IGVtYWlsTWF0Y2ggPSBvdXRwdXQubWF0Y2goL1wiKFteXCJdKylcIi8pO1xuICAgICAgY29uc3QgZGF0ZU1hdGNoID0gb3V0cHV0Lm1hdGNoKC9tYWRlIChcXHcrIFxcdysgXFxkKyBcXGQrOlxcZCs6XFxkKyBcXGQrIFxcdyspLyk7XG4gICAgICBcbiAgICAgIGNvbnN0IHNpZ25lcktleSA9IGtleU1hdGNoID8ga2V5TWF0Y2hbMV0gOiB1bmRlZmluZWQ7XG4gICAgICBjb25zdCBzaWduZXJFbWFpbCA9IGVtYWlsTWF0Y2ggPyBlbWFpbE1hdGNoWzFdIDogdW5kZWZpbmVkO1xuICAgICAgY29uc3Qgc2lnbmF0dXJlRGF0ZSA9IGRhdGVNYXRjaCA/IG5ldyBEYXRlKGRhdGVNYXRjaFsxXSkgOiB1bmRlZmluZWQ7XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGlmIGtleSBpcyB0cnVzdGVkXG4gICAgICBpZiAodGhpcy50cnVzdGVkS2V5cy5zaXplID4gMCAmJiBzaWduZXJLZXkpIHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGtleSBJRCBlbmRzIHdpdGggYW55IG9mIG91ciB0cnVzdGVkIGtleXNcbiAgICAgICAgY29uc3QgaXNUcnVzdGVkID0gQXJyYXkuZnJvbSh0aGlzLnRydXN0ZWRLZXlzKS5zb21lKHRydXN0ZWRLZXkgPT4gXG4gICAgICAgICAgc2lnbmVyS2V5LmVuZHNXaXRoKHRydXN0ZWRLZXkudG9VcHBlckNhc2UoKSlcbiAgICAgICAgKTtcbiAgICAgICAgXG4gICAgICAgIGlmICghaXNUcnVzdGVkKSB7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHZlcmlmaWVkOiBmYWxzZSxcbiAgICAgICAgICAgIHNpZ25lcktleSxcbiAgICAgICAgICAgIHNpZ25lckVtYWlsLFxuICAgICAgICAgICAgc2lnbmF0dXJlRGF0ZSxcbiAgICAgICAgICAgIGVycm9yOiBgU2lnbmF0dXJlIGlzIHZhbGlkIGJ1dCBrZXkgJHtzaWduZXJLZXl9IGlzIG5vdCBpbiB0cnVzdGVkIGtleXMgbGlzdGBcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZlcmlmaWVkOiB0cnVlLFxuICAgICAgICBzaWduZXJLZXksXG4gICAgICAgIHNpZ25lckVtYWlsLFxuICAgICAgICBzaWduYXR1cmVEYXRlXG4gICAgICB9O1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZlcmlmaWVkOiBmYWxzZSxcbiAgICAgICAgZXJyb3I6IGBGYWlsZWQgdG8gdmVyaWZ5IHNpZ25hdHVyZTogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YFxuICAgICAgfTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBWZXJpZnkgYSBmaWxlIGNoZWNrc3VtIGFnYWluc3QgZXhwZWN0ZWQgdmFsdWVcbiAgICogQHBhcmFtIGZpbGVQYXRoIFBhdGggdG8gdGhlIGZpbGUgdG8gdmVyaWZ5XG4gICAqIEBwYXJhbSBleHBlY3RlZENoZWNrc3VtIEV4cGVjdGVkIFNIQTI1NiBjaGVja3N1bVxuICAgKiBAcmV0dXJucyBWZXJpZmljYXRpb24gcmVzdWx0XG4gICAqL1xuICBhc3luYyB2ZXJpZnlDaGVja3N1bShmaWxlUGF0aDogc3RyaW5nLCBleHBlY3RlZENoZWNrc3VtOiBzdHJpbmcpOiBQcm9taXNlPENoZWNrc3VtVmVyaWZpY2F0aW9uUmVzdWx0PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFJlYWQgZmlsZSBhbmQgY2FsY3VsYXRlIGNoZWNrc3VtXG4gICAgICBjb25zdCBmaWxlQnVmZmVyID0gYXdhaXQgZnMucmVhZEZpbGUoZmlsZVBhdGgpO1xuICAgICAgY29uc3QgaGFzaCA9IGNyeXB0by5jcmVhdGVIYXNoKCdzaGEyNTYnKTtcbiAgICAgIGhhc2gudXBkYXRlKGZpbGVCdWZmZXIpO1xuICAgICAgY29uc3QgYWN0dWFsQ2hlY2tzdW0gPSBoYXNoLmRpZ2VzdCgnaGV4Jyk7XG4gICAgICBcbiAgICAgIC8vIENvbXBhcmUgY2hlY2tzdW1zXG4gICAgICBjb25zdCB2ZXJpZmllZCA9IGFjdHVhbENoZWNrc3VtLnRvTG93ZXJDYXNlKCkgPT09IGV4cGVjdGVkQ2hlY2tzdW0udG9Mb3dlckNhc2UoKTtcbiAgICAgIFxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdmVyaWZpZWQsXG4gICAgICAgIGV4cGVjdGVkQ2hlY2tzdW0sXG4gICAgICAgIGFjdHVhbENoZWNrc3VtLFxuICAgICAgICBlcnJvcjogdmVyaWZpZWQgPyB1bmRlZmluZWQgOiAnQ2hlY2tzdW0gbWlzbWF0Y2gnXG4gICAgICB9O1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHZlcmlmaWVkOiBmYWxzZSxcbiAgICAgICAgZXhwZWN0ZWRDaGVja3N1bSxcbiAgICAgICAgZXJyb3I6IGBGYWlsZWQgdG8gdmVyaWZ5IGNoZWNrc3VtOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKX1gXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFZlcmlmeSByZWxlYXNlIGFydGlmYWN0cyB1c2luZyBhIGNoZWNrc3VtcyBmaWxlXG4gICAqIEBwYXJhbSBjaGVja3N1bXNGaWxlIFBhdGggdG8gY2hlY2tzdW1zIGZpbGUgKGUuZy4sIFNIQTI1NlNVTVMpXG4gICAqIEBwYXJhbSBhcnRpZmFjdERpciBEaXJlY3RvcnkgY29udGFpbmluZyBhcnRpZmFjdHMgdG8gdmVyaWZ5XG4gICAqIEByZXR1cm5zIE1hcCBvZiBmaWxlbmFtZSB0byB2ZXJpZmljYXRpb24gcmVzdWx0XG4gICAqL1xuICBhc3luYyB2ZXJpZnlSZWxlYXNlQXJ0aWZhY3RzKFxuICAgIGNoZWNrc3Vtc0ZpbGU6IHN0cmluZywgXG4gICAgYXJ0aWZhY3REaXI6IHN0cmluZ1xuICApOiBQcm9taXNlPE1hcDxzdHJpbmcsIENoZWNrc3VtVmVyaWZpY2F0aW9uUmVzdWx0Pj4ge1xuICAgIGNvbnN0IHJlc3VsdHMgPSBuZXcgTWFwPHN0cmluZywgQ2hlY2tzdW1WZXJpZmljYXRpb25SZXN1bHQ+KCk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIC8vIFJlYWQgY2hlY2tzdW1zIGZpbGVcbiAgICAgIGNvbnN0IGNoZWNrc3Vtc0NvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShjaGVja3N1bXNGaWxlLCAndXRmLTgnKTtcbiAgICAgIGNvbnN0IGxpbmVzID0gY2hlY2tzdW1zQ29udGVudC5zcGxpdCgnXFxuJykuZmlsdGVyKGxpbmUgPT4gbGluZS50cmltKCkpO1xuICAgICAgXG4gICAgICAvLyBQYXJzZSBjaGVja3N1bXMgKGZvcm1hdDogXCJjaGVja3N1bSAgZmlsZW5hbWVcIiBvciBcImNoZWNrc3VtICpmaWxlbmFtZVwiKVxuICAgICAgZm9yIChjb25zdCBsaW5lIG9mIGxpbmVzKSB7XG4gICAgICAgIGNvbnN0IG1hdGNoID0gbGluZS5tYXRjaCgvXihbYS1mMC05XSspXFxzK1xcKj8oLispJC9pKTtcbiAgICAgICAgaWYgKCFtYXRjaCkgY29udGludWU7XG4gICAgICAgIFxuICAgICAgICBjb25zdCBbLCBjaGVja3N1bSwgZmlsZW5hbWVdID0gbWF0Y2g7XG4gICAgICAgIGNvbnN0IGZpbGVQYXRoID0gcGF0aC5qb2luKGFydGlmYWN0RGlyLCBmaWxlbmFtZSk7XG4gICAgICAgIFxuICAgICAgICAvLyBWZXJpZnkgZWFjaCBmaWxlXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMudmVyaWZ5Q2hlY2tzdW0oZmlsZVBhdGgsIGNoZWNrc3VtKTtcbiAgICAgICAgcmVzdWx0cy5zZXQoZmlsZW5hbWUsIHJlc3VsdCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHJldHVybiByZXN1bHRzO1xuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIElmIHdlIGNhbid0IHJlYWQgdGhlIGNoZWNrc3VtcyBmaWxlLCBtYXJrIGFsbCBhcyB1bnZlcmlmaWVkXG4gICAgICByZXN1bHRzLnNldCgnKicsIHtcbiAgICAgICAgdmVyaWZpZWQ6IGZhbHNlLFxuICAgICAgICBlcnJvcjogYEZhaWxlZCB0byByZWFkIGNoZWNrc3VtcyBmaWxlOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKX1gXG4gICAgICB9KTtcbiAgICAgIHJldHVybiByZXN1bHRzO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIEFkZCBhIHRydXN0ZWQga2V5IGZvciBzaWduYXR1cmUgdmVyaWZpY2F0aW9uXG4gICAqIEBwYXJhbSBrZXlJZCBHUEcga2V5IElEIG9yIGZpbmdlcnByaW50XG4gICAqL1xuICBhZGRUcnVzdGVkS2V5KGtleUlkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnRydXN0ZWRLZXlzLmFkZChrZXlJZC50b1VwcGVyQ2FzZSgpKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJlbW92ZSBhIHRydXN0ZWQga2V5XG4gICAqIEBwYXJhbSBrZXlJZCBHUEcga2V5IElEIG9yIGZpbmdlcnByaW50XG4gICAqL1xuICByZW1vdmVUcnVzdGVkS2V5KGtleUlkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnRydXN0ZWRLZXlzLmRlbGV0ZShrZXlJZC50b1VwcGVyQ2FzZSgpKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBsaXN0IG9mIHRydXN0ZWQga2V5c1xuICAgKi9cbiAgZ2V0VHJ1c3RlZEtleXMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMudHJ1c3RlZEtleXMpO1xuICB9XG4gIFxuICAvKipcbiAgICogSW1wb3J0IGEgR1BHIHB1YmxpYyBrZXlcbiAgICogQHBhcmFtIGtleURhdGEgVGhlIHB1YmxpYyBrZXkgZGF0YSB0byBpbXBvcnRcbiAgICogQHJldHVybnMgU3VjY2VzcyBzdGF0dXNcbiAgICovXG4gIGFzeW5jIGltcG9ydFB1YmxpY0tleShrZXlEYXRhOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgLy8gV3JpdGUga2V5IGRhdGEgdG8gdGVtcG9yYXJ5IGZpbGUgd2l0aCBzZWN1cmUgcmFuZG9tIG5hbWVcbiAgICAgIGNvbnN0IHRlbXBGaWxlID0gcGF0aC5qb2luKHByb2Nlc3MuY3dkKCksIGAuZ3BnLWltcG9ydC0ke3JhbmRvbUJ5dGVzKDgpLnRvU3RyaW5nKCdoZXgnKX0uYXNjYCk7XG4gICAgICBhd2FpdCBmcy53cml0ZUZpbGUodGVtcEZpbGUsIGtleURhdGEpO1xuICAgICAgXG4gICAgICB0cnkge1xuICAgICAgICAvLyBJbXBvcnQgdGhlIGtleVxuICAgICAgICBhd2FpdCBzYWZlRXhlYygnZ3BnJywgWyctLWltcG9ydCcsIHRlbXBGaWxlXSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgLy8gQ2xlYW4gdXAgdGVtcCBmaWxlXG4gICAgICAgIGF3YWl0IGZzLnVubGluayh0ZW1wRmlsZSkuY2F0Y2goKCkgPT4ge30pO1xuICAgICAgfVxuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBpbXBvcnQgR1BHIGtleTonLCBlcnJvcik7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG59Il19