UNPKG

35.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const fs_1 = require("fs");
5const child_process_1 = require("child_process");
6const http = require("http");
7const path = require("path");
8const getPort = require("get-port");
9const createDebug = require("debug");
10const command_exists_1 = require("command-exists");
11const glob = require("glob");
12const eol = require("eol");
13const constants_1 = require("./constants");
14const utils_1 = require("./utils");
15const debug = createDebug('devcert');
16// Install the once-per-machine trusted root CA. We'll use this CA to sign per-app certs, allowing
17// us to minimize the need for elevated permissions while still allowing for per-app certificates.
18function installCertificateAuthority(installCertutil) {
19 return tslib_1.__awaiter(this, void 0, void 0, function* () {
20 debug(`generating openssl configuration`);
21 generateOpenSSLConfFiles();
22 debug(`generating root certificate authority key`);
23 utils_1.generateKey(constants_1.rootKeyPath);
24 debug(`generating root certificate authority certificate`);
25 utils_1.openssl(`req -config ${constants_1.opensslConfPath} -key ${constants_1.rootKeyPath} -out ${constants_1.rootCertPath} -new -subj "/CN=devcert" -x509 -days 7000 -extensions v3_ca`);
26 debug(`adding root certificate authority to trust stores`);
27 if (constants_1.isMac) {
28 yield addToMacTrustStores(installCertutil);
29 }
30 else if (constants_1.isLinux) {
31 yield addToLinuxTrustStores(installCertutil);
32 }
33 else {
34 yield addToWindowsTrustStores();
35 }
36 });
37}
38exports.default = installCertificateAuthority;
39// Copy our openssl conf template to the local config folder, and update the paths to be OS
40// specific. Also initializes the files openssl needs to sign certificates as a certificate
41// authority
42function generateOpenSSLConfFiles() {
43 let confTemplate = fs_1.readFileSync(constants_1.opensslConfTemplate, 'utf-8');
44 confTemplate = confTemplate.replace(/DATABASE_PATH/, constants_1.configPath('index.txt').replace(/\\/g, '\\\\'));
45 confTemplate = confTemplate.replace(/SERIAL_PATH/, constants_1.configPath('serial').replace(/\\/g, '\\\\'));
46 confTemplate = eol.auto(confTemplate);
47 fs_1.writeFileSync(constants_1.opensslConfPath, confTemplate);
48 fs_1.writeFileSync(constants_1.configPath('index.txt'), '');
49 fs_1.writeFileSync(constants_1.configPath('serial'), '01');
50 // This version number lets us write code in the future that intelligently upgrades an existing
51 // devcert installation. This "ca-version" is independent of the devcert package version, and
52 // tracks changes to the root certificate setup only.
53 fs_1.writeFileSync(constants_1.configPath('devcert-ca-version'), '1');
54}
55// macOS is pretty simple - just add the certificate to the system keychain, and most applications
56// will delegate to that for determining trusted certificates. Firefox, of course, does it's own
57// thing. We can try to automatically install the cert with Firefox if we can use certutil via the
58// `nss` Homebrew package, otherwise we go manual with user-facing prompts.
59function addToMacTrustStores(installCertutil) {
60 return tslib_1.__awaiter(this, void 0, void 0, function* () {
61 // Chrome, Safari, system utils
62 debug('adding devcert root CA to macOS system keychain');
63 utils_1.run(`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain -p ssl -p basic "${constants_1.rootCertPath}"`);
64 // Firefox
65 try {
66 // Try to use certutil to install the cert automatically
67 debug('adding devcert root CA to firefox');
68 yield addCertificateToNSSCertDB(path.join(process.env.HOME, 'Library/Application Support/Firefox/Profiles/*'), {
69 installCertutil,
70 checkForOpenFirefox: true
71 });
72 }
73 catch (e) {
74 // Otherwise, open the cert in Firefox to install it
75 yield openCertificateInFirefox('/Applications/Firefox.app/Contents/MacOS/firefox');
76 }
77 });
78}
79// Linux is surprisingly difficult. There seems to be multiple system-wide repositories for certs,
80// so we copy ours to each. However, Firefox does it's usual separate trust store. Plus Chrome
81// relies on the NSS tooling (like Firefox), but uses the user's NSS database, unlike Firefox which
82// uses a separate Mozilla one. And since Chrome doesn't prompt the user with a GUI flow when
83// opening certs, if we can't use certutil, we're out of luck.
84function addToLinuxTrustStores(installCertutil) {
85 return tslib_1.__awaiter(this, void 0, void 0, function* () {
86 // system utils
87 debug('adding devcert root CA to linux system-wide certificates');
88 utils_1.run(`sudo cp ${constants_1.rootCertPath} /etc/ssl/certs/devcert.pem`);
89 utils_1.run(`sudo cp ${constants_1.rootCertPath} /usr/local/share/ca-certificates/devcert.cer`);
90 utils_1.run(`sudo update-ca-certificates`);
91 // Firefox
92 try {
93 // Try to use certutil to install the cert automatically
94 debug('adding devcert root CA to firefox');
95 yield addCertificateToNSSCertDB(path.join(process.env.HOME, '.mozilla/firefox/*'), {
96 installCertutil,
97 checkForOpenFirefox: true
98 });
99 }
100 catch (e) {
101 // Otherwise, open the cert in Firefox to install it
102 yield openCertificateInFirefox('firefox');
103 }
104 // Chrome
105 try {
106 debug('adding devcert root CA to chrome');
107 yield addCertificateToNSSCertDB(path.join(process.env.HOME, '.pki/nssdb'), { installCertutil });
108 }
109 catch (e) {
110 console.warn(`
111WARNING: Because you did not pass in \`installCertutil: true\` to devcert, we
112are unable to update Chrome to automatically trust generated development
113certificates. The certificates will work, but Chrome will continue to warn you
114that they are untrusted.`);
115 }
116 });
117}
118// Windows is at least simple. Like macOS, most applications will delegate to the system trust
119// store, which is updated with the confusingly named `certutil` exe (not the same as the
120// NSS/Mozilla certutil). Firefox does it's own thing as usual, and getting a copy of NSS certutil
121// onto the Windows machine to try updating the Firefox store is basically a nightmare, so we don't
122// even try it - we just bail out to the GUI.
123function addToWindowsTrustStores() {
124 return tslib_1.__awaiter(this, void 0, void 0, function* () {
125 // IE, Chrome, system utils
126 debug('adding devcert root to Windows OS trust store');
127 utils_1.run(`certutil -addstore -user root ${constants_1.rootCertPath}`);
128 // Firefox (don't even try NSS certutil, no easy install for Windows)
129 yield openCertificateInFirefox('start firefox');
130 });
131}
132// Given a directory or glob pattern of directories, attempt to install the certificate to each
133// directory containing an NSS database.
134function addCertificateToNSSCertDB(nssDirGlob, options = {}) {
135 return tslib_1.__awaiter(this, void 0, void 0, function* () {
136 let certutilPath = lookupOrInstallCertutil(options.installCertutil);
137 if (!certutilPath) {
138 throw new Error('certutil not available, and `installCertutil` was false');
139 }
140 // Firefox appears to load the NSS database in-memory on startup, and overwrite on exit. So we
141 // have to ask the user to quite Firefox first so our changes don't get overwritten.
142 if (options.checkForOpenFirefox) {
143 let runningProcesses = utils_1.run('ps aux');
144 if (runningProcesses.indexOf('firefox') > -1) {
145 console.log('Please close Firefox before continuing (Press <Enter> when ready)');
146 yield utils_1.waitForUser();
147 }
148 }
149 debug(`trying to install certificate into NSS databases in ${nssDirGlob}`);
150 glob.sync(nssDirGlob).forEach((potentialNSSDBDir) => {
151 debug(`checking to see if ${potentialNSSDBDir} is a valid NSS database directory`);
152 if (fs_1.existsSync(path.join(potentialNSSDBDir, 'cert8.db'))) {
153 debug(`Found legacy NSS database in ${potentialNSSDBDir}, adding devcert ...`);
154 utils_1.run(`${certutilPath} -A -d "${potentialNSSDBDir}" -t 'C,,' -i ${constants_1.rootCertPath} -n devcert`);
155 }
156 else if (fs_1.existsSync(path.join(potentialNSSDBDir, 'cert9.db'))) {
157 debug(`Found modern NSS database in ${potentialNSSDBDir}, adding devcert ...`);
158 utils_1.run(`${certutilPath} -A -d "sql:${potentialNSSDBDir}" -t 'C,,' -i ${constants_1.rootCertPath} -n devcert`);
159 }
160 });
161 });
162}
163// When a Firefox tab is directed to a URL that returns a certificate, it will automatically prompt
164// the user if they want to add it to their trusted certificates. This is handy since Firefox is by
165// far the most troublesome to handle. If we can't automatically install the certificate (because
166// certutil is not available / installable), we instead start a quick web server and host our
167// certificate file. Then we open the hosted cert URL in Firefox to kick off the GUI flow.
168function openCertificateInFirefox(firefoxPath) {
169 return tslib_1.__awaiter(this, void 0, void 0, function* () {
170 debug('adding devert to firefox manually - launch webserver for certificate hosting');
171 let port = yield getPort();
172 let server = http.createServer((req, res) => {
173 res.writeHead(200, { 'Content-type': 'application/x-x509-ca-cert' });
174 res.write(fs_1.readFileSync(constants_1.rootCertPath));
175 res.end();
176 }).listen(port);
177 debug('certificate is hosted, starting firefox at hosted URL');
178 console.log(`Unable to automatically install SSL certificate - please follow the prompts at http://localhost:${port} in Firefox to trust the root certificate`);
179 console.log('See https://github.com/davewasmer/devcert#how-it-works for more details');
180 console.log('-- Press <Enter> once you finish the Firefox prompts --');
181 child_process_1.exec(`${firefoxPath} http://localhost:${port}`);
182 yield utils_1.waitForUser();
183 });
184}
185// Try to install certutil if it's not already available, and return the path to the executable
186function lookupOrInstallCertutil(installCertutil) {
187 debug('looking for nss tooling ...');
188 if (constants_1.isMac) {
189 debug('on mac, looking for homebrew (the only method to install nss that is currently supported by devcert');
190 if (command_exists_1.sync('brew')) {
191 let nssPath;
192 let certutilPath;
193 try {
194 certutilPath = path.join(utils_1.run('brew --prefix nss').toString().trim(), 'bin', 'certutil');
195 }
196 catch (e) {
197 debug('brew was found, but nss is not installed');
198 if (installCertutil) {
199 debug('attempting to install nss via brew');
200 utils_1.run('brew install nss');
201 certutilPath = path.join(utils_1.run('brew --prefix nss').toString().trim(), 'bin', 'certutil');
202 }
203 else {
204 return false;
205 }
206 }
207 debug(`Found nss installed at ${certutilPath}`);
208 return certutilPath;
209 }
210 }
211 else if (constants_1.isLinux) {
212 debug('on linux, checking is nss is already installed');
213 if (!command_exists_1.sync('certutil')) {
214 if (installCertutil) {
215 debug('not already installed, installing it ourselves');
216 utils_1.run('sudo apt install libnss3-tools');
217 }
218 else {
219 debug('not installed and do not want to install');
220 return false;
221 }
222 }
223 debug('looks like nss is installed');
224 return utils_1.run('which certutil').toString().trim();
225 }
226 // Windows? Ha!
227 return false;
228}
229//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9vdC1hdXRob3JpdHkuanMiLCJzb3VyY2VSb290IjoiL1VzZXJzL2phc29ubWlsbGVyL1Byb2plY3RzL2l0YWx5L2RldmNlcnQvIiwic291cmNlcyI6WyJyb290LWF1dGhvcml0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyQkFBNkQ7QUFDN0QsaURBQXFDO0FBQ3JDLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0Isb0NBQW9DO0FBQ3BDLHFDQUFxQztBQUNyQyxtREFBdUQ7QUFDdkQsNkJBQTZCO0FBQzdCLDJCQUEyQjtBQUUzQiwyQ0FTcUI7QUFDckIsbUNBS2lCO0FBRWpCLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVyQyxrR0FBa0c7QUFDbEcsa0dBQWtHO0FBQ2xHLHFDQUEwRCxlQUF3Qjs7UUFDaEYsS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7UUFDMUMsd0JBQXdCLEVBQUUsQ0FBQztRQUUzQixLQUFLLENBQUMsMkNBQTJDLENBQUMsQ0FBQztRQUNuRCxtQkFBVyxDQUFDLHVCQUFXLENBQUMsQ0FBQztRQUV6QixLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQztRQUMzRCxlQUFPLENBQUMsZUFBZ0IsMkJBQWdCLFNBQVUsdUJBQVksU0FBVSx3QkFBYSw4REFBOEQsQ0FBQyxDQUFDO1FBRXJKLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxDQUFBO1FBQzFELEVBQUUsQ0FBQyxDQUFDLGlCQUFLLENBQUMsQ0FBQyxDQUFDO1lBQ1YsTUFBTSxtQkFBbUIsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUM3QyxDQUFDO1FBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLG1CQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ25CLE1BQU0scUJBQXFCLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUFDLElBQUksQ0FBQyxDQUFDO1lBQ04sTUFBTSx1QkFBdUIsRUFBRSxDQUFDO1FBQ2xDLENBQUM7SUFDSCxDQUFDO0NBQUE7QUFsQkQsOENBa0JDO0FBRUQsMkZBQTJGO0FBQzNGLDJGQUEyRjtBQUMzRixZQUFZO0FBQ1o7SUFDRSxJQUFJLFlBQVksR0FBRyxpQkFBWSxDQUFDLCtCQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzlELFlBQVksR0FBRyxZQUFZLENBQUMsT0FBTyxDQUFDLGVBQWUsRUFBRSxzQkFBVSxDQUFDLFdBQVcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUNyRyxZQUFZLEdBQUcsWUFBWSxDQUFDLE9BQU8sQ0FBQyxhQUFhLEVBQUUsc0JBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDaEcsWUFBWSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDdEMsa0JBQWEsQ0FBQywyQkFBZSxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQzdDLGtCQUFhLENBQUMsc0JBQVUsQ0FBQyxXQUFXLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMzQyxrQkFBYSxDQUFDLHNCQUFVLENBQUMsUUFBUSxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDMUMsK0ZBQStGO0lBQy9GLDZGQUE2RjtJQUM3RixxREFBcUQ7SUFDckQsa0JBQWEsQ0FBQyxzQkFBVSxDQUFDLG9CQUFvQixDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7QUFDdkQsQ0FBQztBQUVELGtHQUFrRztBQUNsRyxnR0FBZ0c7QUFDaEcsa0dBQWtHO0FBQ2xHLDJFQUEyRTtBQUMzRSw2QkFBbUMsZUFBd0I7O1FBQ3pELCtCQUErQjtRQUMvQixLQUFLLENBQUMsaURBQWlELENBQUMsQ0FBQztRQUN6RCxXQUFHLENBQUMseUdBQTBHLHdCQUFhLEdBQUcsQ0FBQyxDQUFDO1FBQ2hJLFVBQVU7UUFDVixJQUFJLENBQUM7WUFDSCx3REFBd0Q7WUFDeEQsS0FBSyxDQUFDLG1DQUFtQyxDQUFDLENBQUM7WUFDM0MsTUFBTSx5QkFBeUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLGdEQUFnRCxDQUFDLEVBQUU7Z0JBQzdHLGVBQWU7Z0JBQ2YsbUJBQW1CLEVBQUUsSUFBSTthQUMxQixDQUFDLENBQUM7UUFDTCxDQUFDO1FBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNYLG9EQUFvRDtZQUNwRCxNQUFNLHdCQUF3QixDQUFDLGtEQUFrRCxDQUFDLENBQUM7UUFDckYsQ0FBQztJQUNILENBQUM7Q0FBQTtBQUVELGtHQUFrRztBQUNsRyw4RkFBOEY7QUFDOUYsbUdBQW1HO0FBQ25HLDZGQUE2RjtBQUM3Riw4REFBOEQ7QUFDOUQsK0JBQXFDLGVBQXdCOztRQUMzRCxlQUFlO1FBQ2YsS0FBSyxDQUFDLDBEQUEwRCxDQUFDLENBQUM7UUFDbEUsV0FBRyxDQUFDLFdBQVksd0JBQWEsNkJBQTZCLENBQUMsQ0FBQztRQUM1RCxXQUFHLENBQUMsV0FBWSx3QkFBYSwrQ0FBK0MsQ0FBQyxDQUFDO1FBQzlFLFdBQUcsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO1FBQ25DLFVBQVU7UUFDVixJQUFJLENBQUM7WUFDSCx3REFBd0Q7WUFDeEQsS0FBSyxDQUFDLG1DQUFtQyxDQUFDLENBQUM7WUFDM0MsTUFBTSx5QkFBeUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLG9CQUFvQixDQUFDLEVBQUU7Z0JBQ2pGLGVBQWU7Z0JBQ2YsbUJBQW1CLEVBQUUsSUFBSTthQUMxQixDQUFDLENBQUM7UUFDTCxDQUFDO1FBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNYLG9EQUFvRDtZQUNwRCxNQUFNLHdCQUF3QixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFDRCxTQUFTO1FBQ1QsSUFBSSxDQUFDO1lBQ0gsS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7WUFDMUMsTUFBTSx5QkFBeUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLFlBQVksQ0FBQyxFQUFFLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQztRQUNsRyxDQUFDO1FBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUM7Ozs7eUJBSVEsQ0FBQyxDQUFDO1FBQ3pCLENBQUM7SUFDSCxDQUFDO0NBQUE7QUFFRCw4RkFBOEY7QUFDOUYseUZBQXlGO0FBQ3pGLGtHQUFrRztBQUNsRyxtR0FBbUc7QUFDbkcsNkNBQTZDO0FBQzdDOztRQUNFLDJCQUEyQjtRQUMzQixLQUFLLENBQUMsK0NBQStDLENBQUMsQ0FBQTtRQUN0RCxXQUFHLENBQUMsaUNBQWtDLHdCQUFhLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZELHFFQUFxRTtRQUNyRSxNQUFNLHdCQUF3QixDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQ2xELENBQUM7Q0FBQTtBQUVELCtGQUErRjtBQUMvRix3Q0FBd0M7QUFDeEMsbUNBQXlDLFVBQWtCLEVBQUUsVUFBd0UsRUFBRTs7UUFDckksSUFBSSxZQUFZLEdBQUcsdUJBQXVCLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQ3BFLEVBQUUsQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQztZQUNsQixNQUFNLElBQUksS0FBSyxDQUFDLHlEQUF5RCxDQUFDLENBQUM7UUFDN0UsQ0FBQztRQUNELDhGQUE4RjtRQUM5RixvRkFBb0Y7UUFDcEYsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUMsQ0FBQztZQUNoQyxJQUFJLGdCQUFnQixHQUFHLFdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNyQyxFQUFFLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUM3QyxPQUFPLENBQUMsR0FBRyxDQUFDLG1FQUFtRSxDQUFDLENBQUM7Z0JBQ2pGLE1BQU0sbUJBQVcsRUFBRSxDQUFDO1lBQ3RCLENBQUM7UUFDSCxDQUFDO1FBQ0QsS0FBSyxDQUFDLHVEQUF3RCxVQUFXLEVBQUUsQ0FBQyxDQUFDO1FBQzdFLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsaUJBQWlCO1lBQzlDLEtBQUssQ0FBQyxzQkFBdUIsaUJBQWtCLG9DQUFvQyxDQUFDLENBQUM7WUFDckYsRUFBRSxDQUFDLENBQUMsZUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pELEtBQUssQ0FBQyxnQ0FBaUMsaUJBQWtCLHNCQUFzQixDQUFDLENBQUE7Z0JBQ2hGLFdBQUcsQ0FBQyxHQUFJLFlBQWEsV0FBWSxpQkFBa0IsaUJBQWtCLHdCQUFhLGFBQWEsQ0FBQyxDQUFDO1lBQ25HLENBQUM7WUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsZUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hFLEtBQUssQ0FBQyxnQ0FBaUMsaUJBQWtCLHNCQUFzQixDQUFDLENBQUE7Z0JBQ2hGLFdBQUcsQ0FBQyxHQUFJLFlBQWEsZUFBZ0IsaUJBQWtCLGlCQUFrQix3QkFBYSxhQUFhLENBQUMsQ0FBQztZQUN2RyxDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0NBQUE7QUFFRCxtR0FBbUc7QUFDbkcsbUdBQW1HO0FBQ25HLGlHQUFpRztBQUNqRyw2RkFBNkY7QUFDN0YsMEZBQTBGO0FBQzFGLGtDQUF3QyxXQUFtQjs7UUFDekQsS0FBSyxDQUFDLDhFQUE4RSxDQUFDLENBQUM7UUFDdEYsSUFBSSxJQUFJLEdBQUcsTUFBTSxPQUFPLEVBQUUsQ0FBQztRQUMzQixJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUc7WUFDdEMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxjQUFjLEVBQUUsNEJBQTRCLEVBQUUsQ0FBQyxDQUFDO1lBQ3JFLEdBQUcsQ0FBQyxLQUFLLENBQUMsaUJBQVksQ0FBQyx3QkFBWSxDQUFDLENBQUMsQ0FBQztZQUN0QyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDWixDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDaEIsS0FBSyxDQUFDLHVEQUF1RCxDQUFDLENBQUM7UUFDL0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtR0FBb0csSUFBSywyQ0FBMkMsQ0FBQyxDQUFDO1FBQ2xLLE9BQU8sQ0FBQyxHQUFHLENBQUMseUVBQXlFLENBQUMsQ0FBQztRQUN2RixPQUFPLENBQUMsR0FBRyxDQUFDLHlEQUF5RCxDQUFDLENBQUM7UUFDdkUsb0JBQUksQ0FBQyxHQUFJLFdBQVkscUJBQXNCLElBQUssRUFBRSxDQUFDLENBQUM7UUFDcEQsTUFBTSxtQkFBVyxFQUFFLENBQUM7SUFDdEIsQ0FBQztDQUFBO0FBRUQsK0ZBQStGO0FBQy9GLGlDQUFpQyxlQUF3QjtJQUN2RCxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQTtJQUNwQyxFQUFFLENBQUMsQ0FBQyxpQkFBSyxDQUFDLENBQUMsQ0FBQztRQUNWLEtBQUssQ0FBQyxxR0FBcUcsQ0FBQyxDQUFDO1FBQzdHLEVBQUUsQ0FBQyxDQUFDLHFCQUFhLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzFCLElBQUksT0FBZSxDQUFDO1lBQ3BCLElBQUksWUFBb0IsQ0FBQztZQUN6QixJQUFJLENBQUM7Z0JBQ0gsWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBRyxDQUFDLG1CQUFtQixDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUUsS0FBSyxFQUFFLFVBQVUsQ0FBQyxDQUFDO1lBQzFGLENBQUM7WUFBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNYLEtBQUssQ0FBQywwQ0FBMEMsQ0FBQyxDQUFDO2dCQUNsRCxFQUFFLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDO29CQUNwQixLQUFLLENBQUMsb0NBQW9DLENBQUMsQ0FBQztvQkFDNUMsV0FBRyxDQUFDLGtCQUFrQixDQUFDLENBQUM7b0JBQ3hCLFlBQVksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDMUYsQ0FBQztnQkFBQyxJQUFJLENBQUMsQ0FBQztvQkFDTixNQUFNLENBQUMsS0FBSyxDQUFDO2dCQUNmLENBQUM7WUFDSCxDQUFDO1lBQ0QsS0FBSyxDQUFDLDBCQUEyQixZQUFhLEVBQUUsQ0FBQyxDQUFDO1lBQ2xELE1BQU0sQ0FBQyxZQUFZLENBQUM7UUFDdEIsQ0FBQztJQUNILENBQUM7SUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsbUJBQU8sQ0FBQyxDQUFDLENBQUM7UUFDbkIsS0FBSyxDQUFDLGdEQUFnRCxDQUFDLENBQUM7UUFDeEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxxQkFBYSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUMvQixFQUFFLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDO2dCQUNwQixLQUFLLENBQUMsZ0RBQWdELENBQUMsQ0FBQztnQkFDeEQsV0FBRyxDQUFDLGdDQUFnQyxDQUFDLENBQUM7WUFDeEMsQ0FBQztZQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNOLEtBQUssQ0FBQywwQ0FBMEMsQ0FBQyxDQUFDO2dCQUNsRCxNQUFNLENBQUMsS0FBSyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFDRCxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQztRQUNyQyxNQUFNLENBQUMsV0FBRyxDQUFDLGdCQUFnQixDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDakQsQ0FBQztJQUNELGVBQWU7SUFDZixNQUFNLENBQUMsS0FBSyxDQUFDO0FBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYywgZXhpc3RzU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCB7IGV4ZWMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCAqIGFzIGh0dHAgZnJvbSAnaHR0cCc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgZ2V0UG9ydCBmcm9tICdnZXQtcG9ydCc7XG5pbXBvcnQgKiBhcyBjcmVhdGVEZWJ1ZyBmcm9tICdkZWJ1Zyc7XG5pbXBvcnQgeyBzeW5jIGFzIGNvbW1hbmRFeGlzdHMgfSBmcm9tICdjb21tYW5kLWV4aXN0cyc7XG5pbXBvcnQgKiBhcyBnbG9iIGZyb20gJ2dsb2InO1xuaW1wb3J0ICogYXMgZW9sIGZyb20gJ2VvbCc7XG5cbmltcG9ydCB7XG4gIGlzTWFjLFxuICBpc0xpbnV4LFxuICBpc1dpbmRvd3MsXG4gIGNvbmZpZ1BhdGgsXG4gIHJvb3RLZXlQYXRoLFxuICByb290Q2VydFBhdGgsXG4gIG9wZW5zc2xDb25mUGF0aCxcbiAgb3BlbnNzbENvbmZUZW1wbGF0ZVxufSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQge1xuICBvcGVuc3NsLFxuICBnZW5lcmF0ZUtleSxcbiAgcnVuLFxuICB3YWl0Rm9yVXNlclxufSBmcm9tICcuL3V0aWxzJztcblxuY29uc3QgZGVidWcgPSBjcmVhdGVEZWJ1ZygnZGV2Y2VydCcpO1xuXG4vLyBJbnN0YWxsIHRoZSBvbmNlLXBlci1tYWNoaW5lIHRydXN0ZWQgcm9vdCBDQS4gV2UnbGwgdXNlIHRoaXMgQ0EgdG8gc2lnbiBwZXItYXBwIGNlcnRzLCBhbGxvd2luZ1xuLy8gdXMgdG8gbWluaW1pemUgdGhlIG5lZWQgZm9yIGVsZXZhdGVkIHBlcm1pc3Npb25zIHdoaWxlIHN0aWxsIGFsbG93aW5nIGZvciBwZXItYXBwIGNlcnRpZmljYXRlcy5cbmV4cG9ydCBkZWZhdWx0IGFzeW5jIGZ1bmN0aW9uIGluc3RhbGxDZXJ0aWZpY2F0ZUF1dGhvcml0eShpbnN0YWxsQ2VydHV0aWw6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgZGVidWcoYGdlbmVyYXRpbmcgb3BlbnNzbCBjb25maWd1cmF0aW9uYCk7XG4gIGdlbmVyYXRlT3BlblNTTENvbmZGaWxlcygpO1xuXG4gIGRlYnVnKGBnZW5lcmF0aW5nIHJvb3QgY2VydGlmaWNhdGUgYXV0aG9yaXR5IGtleWApO1xuICBnZW5lcmF0ZUtleShyb290S2V5UGF0aCk7XG5cbiAgZGVidWcoYGdlbmVyYXRpbmcgcm9vdCBjZXJ0aWZpY2F0ZSBhdXRob3JpdHkgY2VydGlmaWNhdGVgKTtcbiAgb3BlbnNzbChgcmVxIC1jb25maWcgJHsgb3BlbnNzbENvbmZQYXRoIH0gLWtleSAkeyByb290S2V5UGF0aCB9IC1vdXQgJHsgcm9vdENlcnRQYXRoIH0gLW5ldyAtc3ViaiBcIi9DTj1kZXZjZXJ0XCIgLXg1MDkgLWRheXMgNzAwMCAtZXh0ZW5zaW9ucyB2M19jYWApO1xuXG4gIGRlYnVnKGBhZGRpbmcgcm9vdCBjZXJ0aWZpY2F0ZSBhdXRob3JpdHkgdG8gdHJ1c3Qgc3RvcmVzYClcbiAgaWYgKGlzTWFjKSB7XG4gICAgYXdhaXQgYWRkVG9NYWNUcnVzdFN0b3JlcyhpbnN0YWxsQ2VydHV0aWwpO1xuICB9IGVsc2UgaWYgKGlzTGludXgpIHtcbiAgICBhd2FpdCBhZGRUb0xpbnV4VHJ1c3RTdG9yZXMoaW5zdGFsbENlcnR1dGlsKTtcbiAgfSBlbHNlIHtcbiAgICBhd2FpdCBhZGRUb1dpbmRvd3NUcnVzdFN0b3JlcygpO1xuICB9XG59XG5cbi8vIENvcHkgb3VyIG9wZW5zc2wgY29uZiB0ZW1wbGF0ZSB0byB0aGUgbG9jYWwgY29uZmlnIGZvbGRlciwgYW5kIHVwZGF0ZSB0aGUgcGF0aHMgdG8gYmUgT1Ncbi8vIHNwZWNpZmljLiBBbHNvIGluaXRpYWxpemVzIHRoZSBmaWxlcyBvcGVuc3NsIG5lZWRzIHRvIHNpZ24gY2VydGlmaWNhdGVzIGFzIGEgY2VydGlmaWNhdGVcbi8vIGF1dGhvcml0eVxuZnVuY3Rpb24gZ2VuZXJhdGVPcGVuU1NMQ29uZkZpbGVzKCkge1xuICBsZXQgY29uZlRlbXBsYXRlID0gcmVhZEZpbGVTeW5jKG9wZW5zc2xDb25mVGVtcGxhdGUsICd1dGYtOCcpO1xuICBjb25mVGVtcGxhdGUgPSBjb25mVGVtcGxhdGUucmVwbGFjZSgvREFUQUJBU0VfUEFUSC8sIGNvbmZpZ1BhdGgoJ2luZGV4LnR4dCcpLnJlcGxhY2UoL1xcXFwvZywgJ1xcXFxcXFxcJykpO1xuICBjb25mVGVtcGxhdGUgPSBjb25mVGVtcGxhdGUucmVwbGFjZSgvU0VSSUFMX1BBVEgvLCBjb25maWdQYXRoKCdzZXJpYWwnKS5yZXBsYWNlKC9cXFxcL2csICdcXFxcXFxcXCcpKTtcbiAgY29uZlRlbXBsYXRlID0gZW9sLmF1dG8oY29uZlRlbXBsYXRlKTtcbiAgd3JpdGVGaWxlU3luYyhvcGVuc3NsQ29uZlBhdGgsIGNvbmZUZW1wbGF0ZSk7XG4gIHdyaXRlRmlsZVN5bmMoY29uZmlnUGF0aCgnaW5kZXgudHh0JyksICcnKTtcbiAgd3JpdGVGaWxlU3luYyhjb25maWdQYXRoKCdzZXJpYWwnKSwgJzAxJyk7XG4gIC8vIFRoaXMgdmVyc2lvbiBudW1iZXIgbGV0cyB1cyB3cml0ZSBjb2RlIGluIHRoZSBmdXR1cmUgdGhhdCBpbnRlbGxpZ2VudGx5IHVwZ3JhZGVzIGFuIGV4aXN0aW5nXG4gIC8vIGRldmNlcnQgaW5zdGFsbGF0aW9uLiBUaGlzIFwiY2EtdmVyc2lvblwiIGlzIGluZGVwZW5kZW50IG9mIHRoZSBkZXZjZXJ0IHBhY2thZ2UgdmVyc2lvbiwgYW5kXG4gIC8vIHRyYWNrcyBjaGFuZ2VzIHRvIHRoZSByb290IGNlcnRpZmljYXRlIHNldHVwIG9ubHkuXG4gIHdyaXRlRmlsZVN5bmMoY29uZmlnUGF0aCgnZGV2Y2VydC1jYS12ZXJzaW9uJyksICcxJyk7XG59XG5cbi8vIG1hY09TIGlzIHByZXR0eSBzaW1wbGUgLSBqdXN0IGFkZCB0aGUgY2VydGlmaWNhdGUgdG8gdGhlIHN5c3RlbSBrZXljaGFpbiwgYW5kIG1vc3QgYXBwbGljYXRpb25zXG4vLyB3aWxsIGRlbGVnYXRlIHRvIHRoYXQgZm9yIGRldGVybWluaW5nIHRydXN0ZWQgY2VydGlmaWNhdGVzLiBGaXJlZm94LCBvZiBjb3Vyc2UsIGRvZXMgaXQncyBvd25cbi8vIHRoaW5nLiBXZSBjYW4gdHJ5IHRvIGF1dG9tYXRpY2FsbHkgaW5zdGFsbCB0aGUgY2VydCB3aXRoIEZpcmVmb3ggaWYgd2UgY2FuIHVzZSBjZXJ0dXRpbCB2aWEgdGhlXG4vLyBgbnNzYCBIb21lYnJldyBwYWNrYWdlLCBvdGhlcndpc2Ugd2UgZ28gbWFudWFsIHdpdGggdXNlci1mYWNpbmcgcHJvbXB0cy5cbmFzeW5jIGZ1bmN0aW9uIGFkZFRvTWFjVHJ1c3RTdG9yZXMoaW5zdGFsbENlcnR1dGlsOiBib29sZWFuKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIENocm9tZSwgU2FmYXJpLCBzeXN0ZW0gdXRpbHNcbiAgZGVidWcoJ2FkZGluZyBkZXZjZXJ0IHJvb3QgQ0EgdG8gbWFjT1Mgc3lzdGVtIGtleWNoYWluJyk7XG4gIHJ1bihgc3VkbyBzZWN1cml0eSBhZGQtdHJ1c3RlZC1jZXJ0IC1kIC1yIHRydXN0Um9vdCAtayAvTGlicmFyeS9LZXljaGFpbnMvU3lzdGVtLmtleWNoYWluIC1wIHNzbCAtcCBiYXNpYyBcIiR7IHJvb3RDZXJ0UGF0aCB9XCJgKTtcbiAgLy8gRmlyZWZveFxuICB0cnkge1xuICAgIC8vIFRyeSB0byB1c2UgY2VydHV0aWwgdG8gaW5zdGFsbCB0aGUgY2VydCBhdXRvbWF0aWNhbGx5XG4gICAgZGVidWcoJ2FkZGluZyBkZXZjZXJ0IHJvb3QgQ0EgdG8gZmlyZWZveCcpO1xuICAgIGF3YWl0IGFkZENlcnRpZmljYXRlVG9OU1NDZXJ0REIocGF0aC5qb2luKHByb2Nlc3MuZW52LkhPTUUsICdMaWJyYXJ5L0FwcGxpY2F0aW9uIFN1cHBvcnQvRmlyZWZveC9Qcm9maWxlcy8qJyksIHtcbiAgICAgIGluc3RhbGxDZXJ0dXRpbCxcbiAgICAgIGNoZWNrRm9yT3BlbkZpcmVmb3g6IHRydWVcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIE90aGVyd2lzZSwgb3BlbiB0aGUgY2VydCBpbiBGaXJlZm94IHRvIGluc3RhbGwgaXRcbiAgICBhd2FpdCBvcGVuQ2VydGlmaWNhdGVJbkZpcmVmb3goJy9BcHBsaWNhdGlvbnMvRmlyZWZveC5hcHAvQ29udGVudHMvTWFjT1MvZmlyZWZveCcpO1xuICB9XG59XG5cbi8vIExpbnV4IGlzIHN1cnByaXNpbmdseSBkaWZmaWN1bHQuIFRoZXJlIHNlZW1zIHRvIGJlIG11bHRpcGxlIHN5c3RlbS13aWRlIHJlcG9zaXRvcmllcyBmb3IgY2VydHMsXG4vLyBzbyB3ZSBjb3B5IG91cnMgdG8gZWFjaC4gSG93ZXZlciwgRmlyZWZveCBkb2VzIGl0J3MgdXN1YWwgc2VwYXJhdGUgdHJ1c3Qgc3RvcmUuIFBsdXMgQ2hyb21lXG4vLyByZWxpZXMgb24gdGhlIE5TUyB0b29saW5nIChsaWtlIEZpcmVmb3gpLCBidXQgdXNlcyB0aGUgdXNlcidzIE5TUyBkYXRhYmFzZSwgdW5saWtlIEZpcmVmb3ggd2hpY2hcbi8vIHVzZXMgYSBzZXBhcmF0ZSBNb3ppbGxhIG9uZS4gQW5kIHNpbmNlIENocm9tZSBkb2Vzbid0IHByb21wdCB0aGUgdXNlciB3aXRoIGEgR1VJIGZsb3cgd2hlblxuLy8gb3BlbmluZyBjZXJ0cywgaWYgd2UgY2FuJ3QgdXNlIGNlcnR1dGlsLCB3ZSdyZSBvdXQgb2YgbHVjay5cbmFzeW5jIGZ1bmN0aW9uIGFkZFRvTGludXhUcnVzdFN0b3JlcyhpbnN0YWxsQ2VydHV0aWw6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gc3lzdGVtIHV0aWxzXG4gIGRlYnVnKCdhZGRpbmcgZGV2Y2VydCByb290IENBIHRvIGxpbnV4IHN5c3RlbS13aWRlIGNlcnRpZmljYXRlcycpO1xuICBydW4oYHN1ZG8gY3AgJHsgcm9vdENlcnRQYXRoIH0gL2V0Yy9zc2wvY2VydHMvZGV2Y2VydC5wZW1gKTtcbiAgcnVuKGBzdWRvIGNwICR7IHJvb3RDZXJ0UGF0aCB9IC91c3IvbG9jYWwvc2hhcmUvY2EtY2VydGlmaWNhdGVzL2RldmNlcnQuY2VyYCk7XG4gIHJ1bihgc3VkbyB1cGRhdGUtY2EtY2VydGlmaWNhdGVzYCk7XG4gIC8vIEZpcmVmb3hcbiAgdHJ5IHtcbiAgICAvLyBUcnkgdG8gdXNlIGNlcnR1dGlsIHRvIGluc3RhbGwgdGhlIGNlcnQgYXV0b21hdGljYWxseVxuICAgIGRlYnVnKCdhZGRpbmcgZGV2Y2VydCByb290IENBIHRvIGZpcmVmb3gnKTtcbiAgICBhd2FpdCBhZGRDZXJ0aWZpY2F0ZVRvTlNTQ2VydERCKHBhdGguam9pbihwcm9jZXNzLmVudi5IT01FLCAnLm1vemlsbGEvZmlyZWZveC8qJyksIHtcbiAgICAgIGluc3RhbGxDZXJ0dXRpbCxcbiAgICAgIGNoZWNrRm9yT3BlbkZpcmVmb3g6IHRydWVcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIE90aGVyd2lzZSwgb3BlbiB0aGUgY2VydCBpbiBGaXJlZm94IHRvIGluc3RhbGwgaXRcbiAgICBhd2FpdCBvcGVuQ2VydGlmaWNhdGVJbkZpcmVmb3goJ2ZpcmVmb3gnKTtcbiAgfVxuICAvLyBDaHJvbWVcbiAgdHJ5IHtcbiAgICBkZWJ1ZygnYWRkaW5nIGRldmNlcnQgcm9vdCBDQSB0byBjaHJvbWUnKTtcbiAgICBhd2FpdCBhZGRDZXJ0aWZpY2F0ZVRvTlNTQ2VydERCKHBhdGguam9pbihwcm9jZXNzLmVudi5IT01FLCAnLnBraS9uc3NkYicpLCB7IGluc3RhbGxDZXJ0dXRpbCB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUud2FybihgXG5XQVJOSU5HOiBCZWNhdXNlIHlvdSBkaWQgbm90IHBhc3MgaW4gXFxgaW5zdGFsbENlcnR1dGlsOiB0cnVlXFxgIHRvIGRldmNlcnQsIHdlXG5hcmUgdW5hYmxlIHRvIHVwZGF0ZSBDaHJvbWUgdG8gYXV0b21hdGljYWxseSB0cnVzdCBnZW5lcmF0ZWQgZGV2ZWxvcG1lbnRcbmNlcnRpZmljYXRlcy4gVGhlIGNlcnRpZmljYXRlcyB3aWxsIHdvcmssIGJ1dCBDaHJvbWUgd2lsbCBjb250aW51ZSB0byB3YXJuIHlvdVxudGhhdCB0aGV5IGFyZSB1bnRydXN0ZWQuYCk7XG4gIH1cbn1cblxuLy8gV2luZG93cyBpcyBhdCBsZWFzdCBzaW1wbGUuIExpa2UgbWFjT1MsIG1vc3QgYXBwbGljYXRpb25zIHdpbGwgZGVsZWdhdGUgdG8gdGhlIHN5c3RlbSB0cnVzdFxuLy8gc3RvcmUsIHdoaWNoIGlzIHVwZGF0ZWQgd2l0aCB0aGUgY29uZnVzaW5nbHkgbmFtZWQgYGNlcnR1dGlsYCBleGUgKG5vdCB0aGUgc2FtZSBhcyB0aGVcbi8vIE5TUy9Nb3ppbGxhIGNlcnR1dGlsKS4gRmlyZWZveCBkb2VzIGl0J3Mgb3duIHRoaW5nIGFzIHVzdWFsLCBhbmQgZ2V0dGluZyBhIGNvcHkgb2YgTlNTIGNlcnR1dGlsXG4vLyBvbnRvIHRoZSBXaW5kb3dzIG1hY2hpbmUgdG8gdHJ5IHVwZGF0aW5nIHRoZSBGaXJlZm94IHN0b3JlIGlzIGJhc2ljYWxseSBhIG5pZ2h0bWFyZSwgc28gd2UgZG9uJ3Rcbi8vIGV2ZW4gdHJ5IGl0IC0gd2UganVzdCBiYWlsIG91dCB0byB0aGUgR1VJLlxuYXN5bmMgZnVuY3Rpb24gYWRkVG9XaW5kb3dzVHJ1c3RTdG9yZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIElFLCBDaHJvbWUsIHN5c3RlbSB1dGlsc1xuICBkZWJ1ZygnYWRkaW5nIGRldmNlcnQgcm9vdCB0byBXaW5kb3dzIE9TIHRydXN0IHN0b3JlJylcbiAgcnVuKGBjZXJ0dXRpbCAtYWRkc3RvcmUgLXVzZXIgcm9vdCAkeyByb290Q2VydFBhdGggfWApO1xuICAvLyBGaXJlZm94IChkb24ndCBldmVuIHRyeSBOU1MgY2VydHV0aWwsIG5vIGVhc3kgaW5zdGFsbCBmb3IgV2luZG93cylcbiAgYXdhaXQgb3BlbkNlcnRpZmljYXRlSW5GaXJlZm94KCdzdGFydCBmaXJlZm94Jyk7XG59XG5cbi8vIEdpdmVuIGEgZGlyZWN0b3J5IG9yIGdsb2IgcGF0dGVybiBvZiBkaXJlY3RvcmllcywgYXR0ZW1wdCB0byBpbnN0YWxsIHRoZSBjZXJ0aWZpY2F0ZSB0byBlYWNoXG4vLyBkaXJlY3RvcnkgY29udGFpbmluZyBhbiBOU1MgZGF0YWJhc2UuXG5hc3luYyBmdW5jdGlvbiBhZGRDZXJ0aWZpY2F0ZVRvTlNTQ2VydERCKG5zc0Rpckdsb2I6IHN0cmluZywgb3B0aW9uczogeyBpbnN0YWxsQ2VydHV0aWw/OiBib29sZWFuLCBjaGVja0Zvck9wZW5GaXJlZm94PzogYm9vbGVhbiB9ID0ge30pOiBQcm9taXNlPHZvaWQ+IHtcbiAgbGV0IGNlcnR1dGlsUGF0aCA9IGxvb2t1cE9ySW5zdGFsbENlcnR1dGlsKG9wdGlvbnMuaW5zdGFsbENlcnR1dGlsKTtcbiAgaWYgKCFjZXJ0dXRpbFBhdGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NlcnR1dGlsIG5vdCBhdmFpbGFibGUsIGFuZCBgaW5zdGFsbENlcnR1dGlsYCB3YXMgZmFsc2UnKTtcbiAgfVxuICAvLyBGaXJlZm94IGFwcGVhcnMgdG8gbG9hZCB0aGUgTlNTIGRhdGFiYXNlIGluLW1lbW9yeSBvbiBzdGFydHVwLCBhbmQgb3ZlcndyaXRlIG9uIGV4aXQuIFNvIHdlXG4gIC8vIGhhdmUgdG8gYXNrIHRoZSB1c2VyIHRvIHF1aXRlIEZpcmVmb3ggZmlyc3Qgc28gb3VyIGNoYW5nZXMgZG9uJ3QgZ2V0IG92ZXJ3cml0dGVuLlxuICBpZiAob3B0aW9ucy5jaGVja0Zvck9wZW5GaXJlZm94KSB7XG4gICAgbGV0IHJ1bm5pbmdQcm9jZXNzZXMgPSBydW4oJ3BzIGF1eCcpO1xuICAgIGlmIChydW5uaW5nUHJvY2Vzc2VzLmluZGV4T2YoJ2ZpcmVmb3gnKSA+IC0xKSB7XG4gICAgICBjb25zb2xlLmxvZygnUGxlYXNlIGNsb3NlIEZpcmVmb3ggYmVmb3JlIGNvbnRpbnVpbmcgKFByZXNzIDxFbnRlcj4gd2hlbiByZWFkeSknKTtcbiAgICAgIGF3YWl0IHdhaXRGb3JVc2VyKCk7XG4gICAgfVxuICB9XG4gIGRlYnVnKGB0cnlpbmcgdG8gaW5zdGFsbCBjZXJ0aWZpY2F0ZSBpbnRvIE5TUyBkYXRhYmFzZXMgaW4gJHsgbnNzRGlyR2xvYiB9YCk7XG4gIGdsb2Iuc3luYyhuc3NEaXJHbG9iKS5mb3JFYWNoKChwb3RlbnRpYWxOU1NEQkRpcikgPT4ge1xuICAgIGRlYnVnKGBjaGVja2luZyB0byBzZWUgaWYgJHsgcG90ZW50aWFsTlNTREJEaXIgfSBpcyBhIHZhbGlkIE5TUyBkYXRhYmFzZSBkaXJlY3RvcnlgKTtcbiAgICBpZiAoZXhpc3RzU3luYyhwYXRoLmpvaW4ocG90ZW50aWFsTlNTREJEaXIsICdjZXJ0OC5kYicpKSkge1xuICAgICAgZGVidWcoYEZvdW5kIGxlZ2FjeSBOU1MgZGF0YWJhc2UgaW4gJHsgcG90ZW50aWFsTlNTREJEaXIgfSwgYWRkaW5nIGRldmNlcnQgLi4uYClcbiAgICAgIHJ1bihgJHsgY2VydHV0aWxQYXRoIH0gLUEgLWQgXCIkeyBwb3RlbnRpYWxOU1NEQkRpciB9XCIgLXQgJ0MsLCcgLWkgJHsgcm9vdENlcnRQYXRoIH0gLW4gZGV2Y2VydGApO1xuICAgIH0gZWxzZSBpZiAoZXhpc3RzU3luYyhwYXRoLmpvaW4ocG90ZW50aWFsTlNTREJEaXIsICdjZXJ0OS5kYicpKSkge1xuICAgICAgZGVidWcoYEZvdW5kIG1vZGVybiBOU1MgZGF0YWJhc2UgaW4gJHsgcG90ZW50aWFsTlNTREJEaXIgfSwgYWRkaW5nIGRldmNlcnQgLi4uYClcbiAgICAgIHJ1bihgJHsgY2VydHV0aWxQYXRoIH0gLUEgLWQgXCJzcWw6JHsgcG90ZW50aWFsTlNTREJEaXIgfVwiIC10ICdDLCwnIC1pICR7IHJvb3RDZXJ0UGF0aCB9IC1uIGRldmNlcnRgKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vLyBXaGVuIGEgRmlyZWZveCB0YWIgaXMgZGlyZWN0ZWQgdG8gYSBVUkwgdGhhdCByZXR1cm5zIGEgY2VydGlmaWNhdGUsIGl0IHdpbGwgYXV0b21hdGljYWxseSBwcm9tcHRcbi8vIHRoZSB1c2VyIGlmIHRoZXkgd2FudCB0byBhZGQgaXQgdG8gdGhlaXIgdHJ1c3RlZCBjZXJ0aWZpY2F0ZXMuIFRoaXMgaXMgaGFuZHkgc2luY2UgRmlyZWZveCBpcyBieVxuLy8gZmFyIHRoZSBtb3N0IHRyb3VibGVzb21lIHRvIGhhbmRsZS4gSWYgd2UgY2FuJ3QgYXV0b21hdGljYWxseSBpbnN0YWxsIHRoZSBjZXJ0aWZpY2F0ZSAoYmVjYXVzZVxuLy8gY2VydHV0aWwgaXMgbm90IGF2YWlsYWJsZSAvIGluc3RhbGxhYmxlKSwgd2UgaW5zdGVhZCBzdGFydCBhIHF1aWNrIHdlYiBzZXJ2ZXIgYW5kIGhvc3Qgb3VyXG4vLyBjZXJ0aWZpY2F0ZSBmaWxlLiBUaGVuIHdlIG9wZW4gdGhlIGhvc3RlZCBjZXJ0IFVSTCBpbiBGaXJlZm94IHRvIGtpY2sgb2ZmIHRoZSBHVUkgZmxvdy5cbmFzeW5jIGZ1bmN0aW9uIG9wZW5DZXJ0aWZpY2F0ZUluRmlyZWZveChmaXJlZm94UGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGRlYnVnKCdhZGRpbmcgZGV2ZXJ0IHRvIGZpcmVmb3ggbWFudWFsbHkgLSBsYXVuY2ggd2Vic2VydmVyIGZvciBjZXJ0aWZpY2F0ZSBob3N0aW5nJyk7XG4gIGxldCBwb3J0ID0gYXdhaXQgZ2V0UG9ydCgpO1xuICBsZXQgc2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoKHJlcSwgcmVzKSA9PiB7XG4gICAgcmVzLndyaXRlSGVhZCgyMDAsIHsgJ0NvbnRlbnQtdHlwZSc6ICdhcHBsaWNhdGlvbi94LXg1MDktY2EtY2VydCcgfSk7XG4gICAgcmVzLndyaXRlKHJlYWRGaWxlU3luYyhyb290Q2VydFBhdGgpKTtcbiAgICByZXMuZW5kKCk7XG4gIH0pLmxpc3Rlbihwb3J0KTtcbiAgZGVidWcoJ2NlcnRpZmljYXRlIGlzIGhvc3RlZCwgc3RhcnRpbmcgZmlyZWZveCBhdCBob3N0ZWQgVVJMJyk7XG4gIGNvbnNvbGUubG9nKGBVbmFibGUgdG8gYXV0b21hdGljYWxseSBpbnN0YWxsIFNTTCBjZXJ0aWZpY2F0ZSAtIHBsZWFzZSBmb2xsb3cgdGhlIHByb21wdHMgYXQgaHR0cDovL2xvY2FsaG9zdDokeyBwb3J0IH0gaW4gRmlyZWZveCB0byB0cnVzdCB0aGUgcm9vdCBjZXJ0aWZpY2F0ZWApO1xuICBjb25zb2xlLmxvZygnU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9kYXZld2FzbWVyL2RldmNlcnQjaG93LWl0LXdvcmtzIGZvciBtb3JlIGRldGFpbHMnKTtcbiAgY29uc29sZS5sb2coJy0tIFByZXNzIDxFbnRlcj4gb25jZSB5b3UgZmluaXNoIHRoZSBGaXJlZm94IHByb21wdHMgLS0nKTtcbiAgZXhlYyhgJHsgZmlyZWZveFBhdGggfSBodHRwOi8vbG9jYWxob3N0OiR7IHBvcnQgfWApO1xuICBhd2FpdCB3YWl0Rm9yVXNlcigpO1xufVxuXG4vLyBUcnkgdG8gaW5zdGFsbCBjZXJ0dXRpbCBpZiBpdCdzIG5vdCBhbHJlYWR5IGF2YWlsYWJsZSwgYW5kIHJldHVybiB0aGUgcGF0aCB0byB0aGUgZXhlY3V0YWJsZVxuZnVuY3Rpb24gbG9va3VwT3JJbnN0YWxsQ2VydHV0aWwoaW5zdGFsbENlcnR1dGlsOiBib29sZWFuKTogYm9vbGVhbiB8IHN0cmluZyB7XG4gIGRlYnVnKCdsb29raW5nIGZvciBuc3MgdG9vbGluZyAuLi4nKVxuICBpZiAoaXNNYWMpIHtcbiAgICBkZWJ1Zygnb24gbWFjLCBsb29raW5nIGZvciBob21lYnJldyAodGhlIG9ubHkgbWV0aG9kIHRvIGluc3RhbGwgbnNzIHRoYXQgaXMgY3VycmVudGx5IHN1cHBvcnRlZCBieSBkZXZjZXJ0Jyk7XG4gICAgaWYgKGNvbW1hbmRFeGlzdHMoJ2JyZXcnKSkge1xuICAgICAgbGV0IG5zc1BhdGg6IHN0cmluZztcbiAgICAgIGxldCBjZXJ0dXRpbFBhdGg6IHN0cmluZztcbiAgICAgIHRyeSB7XG4gICAgICAgIGNlcnR1dGlsUGF0aCA9IHBhdGguam9pbihydW4oJ2JyZXcgLS1wcmVmaXggbnNzJykudG9TdHJpbmcoKS50cmltKCksICdiaW4nLCAnY2VydHV0aWwnKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgZGVidWcoJ2JyZXcgd2FzIGZvdW5kLCBidXQgbnNzIGlzIG5vdCBpbnN0YWxsZWQnKTtcbiAgICAgICAgaWYgKGluc3RhbGxDZXJ0dXRpbCkge1xuICAgICAgICAgIGRlYnVnKCdhdHRlbXB0aW5nIHRvIGluc3RhbGwgbnNzIHZpYSBicmV3Jyk7XG4gICAgICAgICAgcnVuKCdicmV3IGluc3RhbGwgbnNzJyk7XG4gICAgICAgICAgY2VydHV0aWxQYXRoID0gcGF0aC5qb2luKHJ1bignYnJldyAtLXByZWZpeCBuc3MnKS50b1N0cmluZygpLnRyaW0oKSwgJ2JpbicsICdjZXJ0dXRpbCcpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZGVidWcoYEZvdW5kIG5zcyBpbnN0YWxsZWQgYXQgJHsgY2VydHV0aWxQYXRoIH1gKTtcbiAgICAgIHJldHVybiBjZXJ0dXRpbFBhdGg7XG4gICAgfVxuICB9IGVsc2UgaWYgKGlzTGludXgpIHtcbiAgICBkZWJ1Zygnb24gbGludXgsIGNoZWNraW5nIGlzIG5zcyBpcyBhbHJlYWR5IGluc3RhbGxlZCcpO1xuICAgIGlmICghY29tbWFuZEV4aXN0cygnY2VydHV0aWwnKSkge1xuICAgICAgaWYgKGluc3RhbGxDZXJ0dXRpbCkge1xuICAgICAgICBkZWJ1Zygnbm90IGFscmVhZHkgaW5zdGFsbGVkLCBpbnN0YWxsaW5nIGl0IG91cnNlbHZlcycpO1xuICAgICAgICBydW4oJ3N1ZG8gYXB0IGluc3RhbGwgbGlibnNzMy10b29scycpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZGVidWcoJ25vdCBpbnN0YWxsZWQgYW5kIGRvIG5vdCB3YW50IHRvIGluc3RhbGwnKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgICBkZWJ1ZygnbG9va3MgbGlrZSBuc3MgaXMgaW5zdGFsbGVkJyk7XG4gICAgcmV0dXJuIHJ1bignd2hpY2ggY2VydHV0aWwnKS50b1N0cmluZygpLnRyaW0oKTtcbiAgfVxuICAvLyBXaW5kb3dzPyBIYSFcbiAgcmV0dXJuIGZhbHNlO1xufSJdfQ==
\No newline at end of file