1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const fs_1 = require("fs");
|
5 | const child_process_1 = require("child_process");
|
6 | const http = require("http");
|
7 | const path = require("path");
|
8 | const getPort = require("get-port");
|
9 | const createDebug = require("debug");
|
10 | const command_exists_1 = require("command-exists");
|
11 | const glob = require("glob");
|
12 | const eol = require("eol");
|
13 | const constants_1 = require("./constants");
|
14 | const utils_1 = require("./utils");
|
15 | const 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.
|
18 | function 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 | }
|
38 | exports.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
|
42 | function 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.
|
59 | function 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.
|
84 | function 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(`
|
111 | WARNING: Because you did not pass in \`installCertutil: true\` to devcert, we
|
112 | are unable to update Chrome to automatically trust generated development
|
113 | certificates. The certificates will work, but Chrome will continue to warn you
|
114 | that 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.
|
123 | function 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.
|
134 | function 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.
|
168 | function 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
|
186 | function 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9vdC1hdXRob3JpdHkuanMiLCJzb3VyY2VSb290IjoiL1VzZXJzL2Rhdy9vc3MvZGV2Y2VydC8iLCJzb3VyY2VzIjpbInJvb3QtYXV0aG9yaXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDJCQUE2RDtBQUM3RCxpREFBcUM7QUFDckMsNkJBQTZCO0FBQzdCLDZCQUE2QjtBQUM3QixvQ0FBb0M7QUFDcEMscUNBQXFDO0FBQ3JDLG1EQUF1RDtBQUN2RCw2QkFBNkI7QUFDN0IsMkJBQTJCO0FBRTNCLDJDQVNxQjtBQUNyQixtQ0FLaUI7QUFFakIsTUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBRXJDLGtHQUFrRztBQUNsRyxrR0FBa0c7QUFDbEcscUNBQTBELGVBQXdCOztRQUNoRixLQUFLLENBQUMsa0NBQWtDLENBQUMsQ0FBQztRQUMxQyx3QkFBd0IsRUFBRSxDQUFDO1FBRTNCLEtBQUssQ0FBQywyQ0FBMkMsQ0FBQyxDQUFDO1FBQ25ELG1CQUFXLENBQUMsdUJBQVcsQ0FBQyxDQUFDO1FBRXpCLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxDQUFDO1FBQzNELGVBQU8sQ0FBQyxlQUFnQiwyQkFBZ0IsU0FBVSx1QkFBWSxTQUFVLHdCQUFhLDhEQUE4RCxDQUFDLENBQUM7UUFFckosS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUE7UUFDMUQsRUFBRSxDQUFDLENBQUMsaUJBQUssQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLG1CQUFtQixDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQzdDLENBQUM7UUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsbUJBQU8sQ0FBQyxDQUFDLENBQUM7WUFDbkIsTUFBTSxxQkFBcUIsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUMvQyxDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDTixNQUFNLHVCQUF1QixFQUFFLENBQUM7UUFDbEMsQ0FBQztJQUNILENBQUM7Q0FBQTtBQWxCRCw4Q0Fr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QUFJLEVBQUUsQ0FBQztJQUNqRCxDQUFDO0lBQ0QsZUFBZTtJQUNmLE1BQU0sQ0FBQyxLQUFLLENBQUM7QUFDZixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jLCBleGlzdHNTeW5jIH0gZnJvbSAnZnMnO1xuaW1wb3J0IHsgZXhlYyB9IGZyb20gJ2NoaWxkX3Byb2Nlc3MnO1xuaW1wb3J0ICogYXMgaHR0cCBmcm9tICdodHRwJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBnZXRQb3J0IGZyb20gJ2dldC1wb3J0JztcbmltcG9ydCAqIGFzIGNyZWF0ZURlYnVnIGZyb20gJ2RlYnVnJztcbmltcG9ydCB7IHN5bmMgYXMgY29tbWFuZEV4aXN0cyB9IGZyb20gJ2NvbW1hbmQtZXhpc3RzJztcbmltcG9ydCAqIGFzIGdsb2IgZnJvbSAnZ2xvYic7XG5pbXBvcnQgKiBhcyBlb2wgZnJvbSAnZW9sJztcblxuaW1wb3J0IHtcbiAgaXNNYWMsXG4gIGlzTGludXgsXG4gIGlzV2luZG93cyxcbiAgY29uZmlnUGF0aCxcbiAgcm9vdEtleVBhdGgsXG4gIHJvb3RDZXJ0UGF0aCxcbiAgb3BlbnNzbENvbmZQYXRoLFxuICBvcGVuc3NsQ29uZlRlbXBsYXRlXG59IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCB7XG4gIG9wZW5zc2wsXG4gIGdlbmVyYXRlS2V5LFxuICBydW4sXG4gIHdhaXRGb3JVc2VyXG59IGZyb20gJy4vdXRpbHMnO1xuXG5jb25zdCBkZWJ1ZyA9IGNyZWF0ZURlYnVnKCdkZXZjZXJ0Jyk7XG5cbi8vIEluc3RhbGwgdGhlIG9uY2UtcGVyLW1hY2hpbmUgdHJ1c3RlZCByb290IENBLiBXZSdsbCB1c2UgdGhpcyBDQSB0byBzaWduIHBlci1hcHAgY2VydHMsIGFsbG93aW5nXG4vLyB1cyB0byBtaW5pbWl6ZSB0aGUgbmVlZCBmb3IgZWxldmF0ZWQgcGVybWlzc2lvbnMgd2hpbGUgc3RpbGwgYWxsb3dpbmcgZm9yIHBlci1hcHAgY2VydGlmaWNhdGVzLlxuZXhwb3J0IGRlZmF1bHQgYXN5bmMgZnVuY3Rpb24gaW5zdGFsbENlcnRpZmljYXRlQXV0aG9yaXR5KGluc3RhbGxDZXJ0dXRpbDogYm9vbGVhbik6IFByb21pc2U8dm9pZD4ge1xuICBkZWJ1ZyhgZ2VuZXJhdGluZyBvcGVuc3NsIGNvbmZpZ3VyYXRpb25gKTtcbiAgZ2VuZXJhdGVPcGVuU1NMQ29uZkZpbGVzKCk7XG5cbiAgZGVidWcoYGdlbmVyYXRpbmcgcm9vdCBjZXJ0aWZpY2F0ZSBhdXRob3JpdHkga2V5YCk7XG4gIGdlbmVyYXRlS2V5KHJvb3RLZXlQYXRoKTtcblxuICBkZWJ1ZyhgZ2VuZXJhdGluZyByb290IGNlcnRpZmljYXRlIGF1dGhvcml0eSBjZXJ0aWZpY2F0ZWApO1xuICBvcGVuc3NsKGByZXEgLWNvbmZpZyAkeyBvcGVuc3NsQ29uZlBhdGggfSAta2V5ICR7IHJvb3RLZXlQYXRoIH0gLW91dCAkeyByb290Q2VydFBhdGggfSAtbmV3IC1zdWJqIFwiL0NOPWRldmNlcnRcIiAteDUwOSAtZGF5cyA3MDAwIC1leHRlbnNpb25zIHYzX2NhYCk7XG5cbiAgZGVidWcoYGFkZGluZyByb290IGNlcnRpZmljYXRlIGF1dGhvcml0eSB0byB0cnVzdCBzdG9yZXNgKVxuICBpZiAoaXNNYWMpIHtcbiAgICBhd2FpdCBhZGRUb01hY1RydXN0U3RvcmVzKGluc3RhbGxDZXJ0dXRpbCk7XG4gIH0gZWxzZSBpZiAoaXNMaW51eCkge1xuICAgIGF3YWl0IGFkZFRvTGludXhUcnVzdFN0b3JlcyhpbnN0YWxsQ2VydHV0aWwpO1xuICB9IGVsc2Uge1xuICAgIGF3YWl0IGFkZFRvV2luZG93c1RydXN0U3RvcmVzKCk7XG4gIH1cbn1cblxuLy8gQ29weSBvdXIgb3BlbnNzbCBjb25mIHRlbXBsYXRlIHRvIHRoZSBsb2NhbCBjb25maWcgZm9sZGVyLCBhbmQgdXBkYXRlIHRoZSBwYXRocyB0byBiZSBPU1xuLy8gc3BlY2lmaWMuIEFsc28gaW5pdGlhbGl6ZXMgdGhlIGZpbGVzIG9wZW5zc2wgbmVlZHMgdG8gc2lnbiBjZXJ0aWZpY2F0ZXMgYXMgYSBjZXJ0aWZpY2F0ZVxuLy8gYXV0aG9yaXR5XG5mdW5jdGlvbiBnZW5lcmF0ZU9wZW5TU0xDb25mRmlsZXMoKSB7XG4gIGxldCBjb25mVGVtcGxhdGUgPSByZWFkRmlsZVN5bmMob3BlbnNzbENvbmZUZW1wbGF0ZSwgJ3V0Zi04Jyk7XG4gIGNvbmZUZW1wbGF0ZSA9IGNvbmZUZW1wbGF0ZS5yZXBsYWNlKC9EQVRBQkFTRV9QQVRILywgY29uZmlnUGF0aCgnaW5kZXgudHh0JykucmVwbGFjZSgvXFxcXC9nLCAnXFxcXFxcXFwnKSk7XG4gIGNvbmZUZW1wbGF0ZSA9IGNvbmZUZW1wbGF0ZS5yZXBsYWNlKC9TRVJJQUxfUEFUSC8sIGNvbmZpZ1BhdGgoJ3NlcmlhbCcpLnJlcGxhY2UoL1xcXFwvZywgJ1xcXFxcXFxcJykpO1xuICBjb25mVGVtcGxhdGUgPSBlb2wuYXV0byhjb25mVGVtcGxhdGUpO1xuICB3cml0ZUZpbGVTeW5jKG9wZW5zc2xDb25mUGF0aCwgY29uZlRlbXBsYXRlKTtcbiAgd3JpdGVGaWxlU3luYyhjb25maWdQYXRoKCdpbmRleC50eHQnKSwgJycpO1xuICB3cml0ZUZpbGVTeW5jKGNvbmZpZ1BhdGgoJ3NlcmlhbCcpLCAnMDEnKTtcbiAgLy8gVGhpcyB2ZXJzaW9uIG51bWJlciBsZXRzIHVzIHdyaXRlIGNvZGUgaW4gdGhlIGZ1dHVyZSB0aGF0IGludGVsbGlnZW50bHkgdXBncmFkZXMgYW4gZXhpc3RpbmdcbiAgLy8gZGV2Y2VydCBpbnN0YWxsYXRpb24uIFRoaXMgXCJjYS12ZXJzaW9uXCIgaXMgaW5kZXBlbmRlbnQgb2YgdGhlIGRldmNlcnQgcGFja2FnZSB2ZXJzaW9uLCBhbmRcbiAgLy8gdHJhY2tzIGNoYW5nZXMgdG8gdGhlIHJvb3QgY2VydGlmaWNhdGUgc2V0dXAgb25seS5cbiAgd3JpdGVGaWxlU3luYyhjb25maWdQYXRoKCdkZXZjZXJ0LWNhLXZlcnNpb24nKSwgJzEnKTtcbn1cblxuLy8gbWFjT1MgaXMgcHJldHR5IHNpbXBsZSAtIGp1c3QgYWRkIHRoZSBjZXJ0aWZpY2F0ZSB0byB0aGUgc3lzdGVtIGtleWNoYWluLCBhbmQgbW9zdCBhcHBsaWNhdGlvbnNcbi8vIHdpbGwgZGVsZWdhdGUgdG8gdGhhdCBmb3IgZGV0ZXJtaW5pbmcgdHJ1c3RlZCBjZXJ0aWZpY2F0ZXMuIEZpcmVmb3gsIG9mIGNvdXJzZSwgZG9lcyBpdCdzIG93blxuLy8gdGhpbmcuIFdlIGNhbiB0cnkgdG8gYXV0b21hdGljYWxseSBpbnN0YWxsIHRoZSBjZXJ0IHdpdGggRmlyZWZveCBpZiB3ZSBjYW4gdXNlIGNlcnR1dGlsIHZpYSB0aGVcbi8vIGBuc3NgIEhvbWVicmV3IHBhY2thZ2UsIG90aGVyd2lzZSB3ZSBnbyBtYW51YWwgd2l0aCB1c2VyLWZhY2luZyBwcm9tcHRzLlxuYXN5bmMgZnVuY3Rpb24gYWRkVG9NYWNUcnVzdFN0b3JlcyhpbnN0YWxsQ2VydHV0aWw6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gQ2hyb21lLCBTYWZhcmksIHN5c3RlbSB1dGlsc1xuICBkZWJ1ZygnYWRkaW5nIGRldmNlcnQgcm9vdCBDQSB0byBtYWNPUyBzeXN0ZW0ga2V5Y2hhaW4nKTtcbiAgcnVuKGBzdWRvIHNlY3VyaXR5IGFkZC10cnVzdGVkLWNlcnQgLWQgLXIgdHJ1c3RSb290IC1rIC9MaWJyYXJ5L0tleWNoYWlucy9TeXN0ZW0ua2V5Y2hhaW4gLXAgc3NsIC1wIGJhc2ljIFwiJHsgcm9vdENlcnRQYXRoIH1cImApO1xuICAvLyBGaXJlZm94XG4gIHRyeSB7XG4gICAgLy8gVHJ5IHRvIHVzZSBjZXJ0dXRpbCB0byBpbnN0YWxsIHRoZSBjZXJ0IGF1dG9tYXRpY2FsbHlcbiAgICBkZWJ1ZygnYWRkaW5nIGRldmNlcnQgcm9vdCBDQSB0byBmaXJlZm94Jyk7XG4gICAgYXdhaXQgYWRkQ2VydGlmaWNhdGVUb05TU0NlcnREQihwYXRoLmpvaW4ocHJvY2Vzcy5lbnYuSE9NRSwgJ0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9GaXJlZm94L1Byb2ZpbGVzLyonKSwge1xuICAgICAgaW5zdGFsbENlcnR1dGlsLFxuICAgICAgY2hlY2tGb3JPcGVuRmlyZWZveDogdHJ1ZVxuICAgIH0pO1xuICB9IGNhdGNoIChlKSB7XG4gICAgLy8gT3RoZXJ3aXNlLCBvcGVuIHRoZSBjZXJ0IGluIEZpcmVmb3ggdG8gaW5zdGFsbCBpdFxuICAgIGF3YWl0IG9wZW5DZXJ0aWZpY2F0ZUluRmlyZWZveCgnL0FwcGxpY2F0aW9ucy9GaXJlZm94LmFwcC9Db250ZW50cy9NYWNPUy9maXJlZm94Jyk7XG4gIH1cbn1cblxuLy8gTGludXggaXMgc3VycHJpc2luZ2x5IGRpZmZpY3VsdC4gVGhlcmUgc2VlbXMgdG8gYmUgbXVsdGlwbGUgc3lzdGVtLXdpZGUgcmVwb3NpdG9yaWVzIGZvciBjZXJ0cyxcbi8vIHNvIHdlIGNvcHkgb3VycyB0byBlYWNoLiBIb3dldmVyLCBGaXJlZm94IGRvZXMgaXQncyB1c3VhbCBzZXBhcmF0ZSB0cnVzdCBzdG9yZS4gUGx1cyBDaHJvbWVcbi8vIHJlbGllcyBvbiB0aGUgTlNTIHRvb2xpbmcgKGxpa2UgRmlyZWZveCksIGJ1dCB1c2VzIHRoZSB1c2VyJ3MgTlNTIGRhdGFiYXNlLCB1bmxpa2UgRmlyZWZveCB3aGljaFxuLy8gdXNlcyBhIHNlcGFyYXRlIE1vemlsbGEgb25lLiBBbmQgc2luY2UgQ2hyb21lIGRvZXNuJ3QgcHJvbXB0IHRoZSB1c2VyIHdpdGggYSBHVUkgZmxvdyB3aGVuXG4vLyBvcGVuaW5nIGNlcnRzLCBpZiB3ZSBjYW4ndCB1c2UgY2VydHV0aWwsIHdlJ3JlIG91dCBvZiBsdWNrLlxuYXN5bmMgZnVuY3Rpb24gYWRkVG9MaW51eFRydXN0U3RvcmVzKGluc3RhbGxDZXJ0dXRpbDogYm9vbGVhbik6IFByb21pc2U8dm9pZD4ge1xuICAvLyBzeXN0ZW0gdXRpbHNcbiAgZGVidWcoJ2FkZGluZyBkZXZjZXJ0IHJvb3QgQ0EgdG8gbGludXggc3lzdGVtLXdpZGUgY2VydGlmaWNhdGVzJyk7XG4gIHJ1bihgc3VkbyBjcCAkeyByb290Q2VydFBhdGggfSAvZXRjL3NzbC9jZXJ0cy9kZXZjZXJ0LnBlbWApO1xuICBydW4oYHN1ZG8gY3AgJHsgcm9vdENlcnRQYXRoIH0gL3Vzci9sb2NhbC9zaGFyZS9jYS1jZXJ0aWZpY2F0ZXMvZGV2Y2VydC5jZXJgKTtcbiAgcnVuKGBzdWRvIHVwZGF0ZS1jYS1jZXJ0aWZpY2F0ZXNgKTtcbiAgLy8gRmlyZWZveFxuICB0cnkge1xuICAgIC8vIFRyeSB0byB1c2UgY2VydHV0aWwgdG8gaW5zdGFsbCB0aGUgY2VydCBhdXRvbWF0aWNhbGx5XG4gICAgZGVidWcoJ2FkZGluZyBkZXZjZXJ0IHJvb3QgQ0EgdG8gZmlyZWZveCcpO1xuICAgIGF3YWl0IGFkZENlcnRpZmljYXRlVG9OU1NDZXJ0REIocGF0aC5qb2luKHByb2Nlc3MuZW52LkhPTUUsICcubW96aWxsYS9maXJlZm94LyonKSwge1xuICAgICAgaW5zdGFsbENlcnR1dGlsLFxuICAgICAgY2hlY2tGb3JPcGVuRmlyZWZveDogdHJ1ZVxuICAgIH0pO1xuICB9IGNhdGNoIChlKSB7XG4gICAgLy8gT3RoZXJ3aXNlLCBvcGVuIHRoZSBjZXJ0IGluIEZpcmVmb3ggdG8gaW5zdGFsbCBpdFxuICAgIGF3YWl0IG9wZW5DZXJ0aWZpY2F0ZUluRmlyZWZveCgnZmlyZWZveCcpO1xuICB9XG4gIC8vIENocm9tZVxuICB0cnkge1xuICAgIGRlYnVnKCdhZGRpbmcgZGV2Y2VydCByb290IENBIHRvIGNocm9tZScpO1xuICAgIGF3YWl0IGFkZENlcnRpZmljYXRlVG9OU1NDZXJ0REIocGF0aC5qb2luKHByb2Nlc3MuZW52LkhPTUUsICcucGtpL25zc2RiJyksIHsgaW5zdGFsbENlcnR1dGlsIH0pO1xuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc29sZS53YXJuKGBcbldBUk5JTkc6IEJlY2F1c2UgeW91IGRpZCBub3QgcGFzcyBpbiBcXGBpbnN0YWxsQ2VydHV0aWw6IHRydWVcXGAgdG8gZGV2Y2VydCwgd2VcbmFyZSB1bmFibGUgdG8gdXBkYXRlIENocm9tZSB0byBhdXRvbWF0aWNhbGx5IHRydXN0IGdlbmVyYXRlZCBkZXZlbG9wbWVudFxuY2VydGlmaWNhdGVzLiBUaGUgY2VydGlmaWNhdGVzIHdpbGwgd29yaywgYnV0IENocm9tZSB3aWxsIGNvbnRpbnVlIHRvIHdhcm4geW91XG50aGF0IHRoZXkgYXJlIHVudHJ1c3RlZC5gKTtcbiAgfVxufVxuXG4vLyBXaW5kb3dzIGlzIGF0IGxlYXN0IHNpbXBsZS4gTGlrZSBtYWNPUywgbW9zdCBhcHBsaWNhdGlvbnMgd2lsbCBkZWxlZ2F0ZSB0byB0aGUgc3lzdGVtIHRydXN0XG4vLyBzdG9yZSwgd2hpY2ggaXMgdXBkYXRlZCB3aXRoIHRoZSBjb25mdXNpbmdseSBuYW1lZCBgY2VydHV0aWxgIGV4ZSAobm90IHRoZSBzYW1lIGFzIHRoZVxuLy8gTlNTL01vemlsbGEgY2VydHV0aWwpLiBGaXJlZm94IGRvZXMgaXQncyBvd24gdGhpbmcgYXMgdXN1YWwsIGFuZCBnZXR0aW5nIGEgY29weSBvZiBOU1MgY2VydHV0aWxcbi8vIG9udG8gdGhlIFdpbmRvd3MgbWFjaGluZSB0byB0cnkgdXBkYXRpbmcgdGhlIEZpcmVmb3ggc3RvcmUgaXMgYmFzaWNhbGx5IGEgbmlnaHRtYXJlLCBzbyB3ZSBkb24ndFxuLy8gZXZlbiB0cnkgaXQgLSB3ZSBqdXN0IGJhaWwgb3V0IHRvIHRoZSBHVUkuXG5hc3luYyBmdW5jdGlvbiBhZGRUb1dpbmRvd3NUcnVzdFN0b3JlcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gSUUsIENocm9tZSwgc3lzdGVtIHV0aWxzXG4gIGRlYnVnKCdhZGRpbmcgZGV2Y2VydCByb290IHRvIFdpbmRvd3MgT1MgdHJ1c3Qgc3RvcmUnKVxuICBydW4oYGNlcnR1dGlsIC1hZGRzdG9yZSAtdXNlciByb290ICR7IHJvb3RDZXJ0UGF0aCB9YCk7XG4gIC8vIEZpcmVmb3ggKGRvbid0IGV2ZW4gdHJ5IE5TUyBjZXJ0dXRpbCwgbm8gZWFzeSBpbnN0YWxsIGZvciBXaW5kb3dzKVxuICBhd2FpdCBvcGVuQ2VydGlmaWNhdGVJbkZpcmVmb3goJ3N0YXJ0IGZpcmVmb3gnKTtcbn1cblxuLy8gR2l2ZW4gYSBkaXJlY3Rvcnkgb3IgZ2xvYiBwYXR0ZXJuIG9mIGRpcmVjdG9yaWVzLCBhdHRlbXB0IHRvIGluc3RhbGwgdGhlIGNlcnRpZmljYXRlIHRvIGVhY2hcbi8vIGRpcmVjdG9yeSBjb250YWluaW5nIGFuIE5TUyBkYXRhYmFzZS5cbmFzeW5jIGZ1bmN0aW9uIGFkZENlcnRpZmljYXRlVG9OU1NDZXJ0REIobnNzRGlyR2xvYjogc3RyaW5nLCBvcHRpb25zOiB7IGluc3RhbGxDZXJ0dXRpbD86IGJvb2xlYW4sIGNoZWNrRm9yT3BlbkZpcmVmb3g/OiBib29sZWFuIH0gPSB7fSk6IFByb21pc2U8dm9pZD4ge1xuICBsZXQgY2VydHV0aWxQYXRoID0gbG9va3VwT3JJbnN0YWxsQ2VydHV0aWwob3B0aW9ucy5pbnN0YWxsQ2VydHV0aWwpO1xuICBpZiAoIWNlcnR1dGlsUGF0aCkge1xuICAgIHRocm93IG5ldyBFcnJvcignY2VydHV0aWwgbm90IGF2YWlsYWJsZSwgYW5kIGBpbnN0YWxsQ2VydHV0aWxgIHdhcyBmYWxzZScpO1xuICB9XG4gIC8vIEZpcmVmb3ggYXBwZWFycyB0byBsb2FkIHRoZSBOU1MgZGF0YWJhc2UgaW4tbWVtb3J5IG9uIHN0YXJ0dXAsIGFuZCBvdmVyd3JpdGUgb24gZXhpdC4gU28gd2VcbiAgLy8gaGF2ZSB0byBhc2sgdGhlIHVzZXIgdG8gcXVpdGUgRmlyZWZveCBmaXJzdCBzbyBvdXIgY2hhbmdlcyBkb24ndCBnZXQgb3ZlcndyaXR0ZW4uXG4gIGlmIChvcHRpb25zLmNoZWNrRm9yT3BlbkZpcmVmb3gpIHtcbiAgICBsZXQgcnVubmluZ1Byb2Nlc3NlcyA9IHJ1bigncHMgYXV4Jyk7XG4gICAgaWYgKHJ1bm5pbmdQcm9jZXNzZXMuaW5kZXhPZignZmlyZWZveCcpID4gLTEpIHtcbiAgICAgIGNvbnNvbGUubG9nKCdQbGVhc2UgY2xvc2UgRmlyZWZveCBiZWZvcmUgY29udGludWluZyAoUHJlc3MgPEVudGVyPiB3aGVuIHJlYWR5KScpO1xuICAgICAgYXdhaXQgd2FpdEZvclVzZXIoKTtcbiAgICB9XG4gIH1cbiAgZGVidWcoYHRyeWluZyB0byBpbnN0YWxsIGNlcnRpZmljYXRlIGludG8gTlNTIGRhdGFiYXNlcyBpbiAkeyBuc3NEaXJHbG9iIH1gKTtcbiAgZ2xvYi5zeW5jKG5zc0Rpckdsb2IpLmZvckVhY2goKHBvdGVudGlhbE5TU0RCRGlyKSA9PiB7XG4gICAgZGVidWcoYGNoZWNraW5nIHRvIHNlZSBpZiAkeyBwb3RlbnRpYWxOU1NEQkRpciB9IGlzIGEgdmFsaWQgTlNTIGRhdGFiYXNlIGRpcmVjdG9yeWApO1xuICAgIGlmIChleGlzdHNTeW5jKHBhdGguam9pbihwb3RlbnRpYWxOU1NEQkRpciwgJ2NlcnQ4LmRiJykpKSB7XG4gICAgICBkZWJ1ZyhgRm91bmQgbGVnYWN5IE5TUyBkYXRhYmFzZSBpbiAkeyBwb3RlbnRpYWxOU1NEQkRpciB9LCBhZGRpbmcgZGV2Y2VydCAuLi5gKVxuICAgICAgcnVuKGAkeyBjZXJ0dXRpbFBhdGggfSAtQSAtZCBcIiR7IHBvdGVudGlhbE5TU0RCRGlyIH1cIiAtdCAnQywsJyAtaSAkeyByb290Q2VydFBhdGggfSAtbiBkZXZjZXJ0YCk7XG4gICAgfSBlbHNlIGlmIChleGlzdHNTeW5jKHBhdGguam9pbihwb3RlbnRpYWxOU1NEQkRpciwgJ2NlcnQ5LmRiJykpKSB7XG4gICAgICBkZWJ1ZyhgRm91bmQgbW9kZXJuIE5TUyBkYXRhYmFzZSBpbiAkeyBwb3RlbnRpYWxOU1NEQkRpciB9LCBhZGRpbmcgZGV2Y2VydCAuLi5gKVxuICAgICAgcnVuKGAkeyBjZXJ0dXRpbFBhdGggfSAtQSAtZCBcInNxbDokeyBwb3RlbnRpYWxOU1NEQkRpciB9XCIgLXQgJ0MsLCcgLWkgJHsgcm9vdENlcnRQYXRoIH0gLW4gZGV2Y2VydGApO1xuICAgIH1cbiAgfSk7XG59XG5cbi8vIFdoZW4gYSBGaXJlZm94IHRhYiBpcyBkaXJlY3RlZCB0byBhIFVSTCB0aGF0IHJldHVybnMgYSBjZXJ0aWZpY2F0ZSwgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IHByb21wdFxuLy8gdGhlIHVzZXIgaWYgdGhleSB3YW50IHRvIGFkZCBpdCB0byB0aGVpciB0cnVzdGVkIGNlcnRpZmljYXRlcy4gVGhpcyBpcyBoYW5keSBzaW5jZSBGaXJlZm94IGlzIGJ5XG4vLyBmYXIgdGhlIG1vc3QgdHJvdWJsZXNvbWUgdG8gaGFuZGxlLiBJZiB3ZSBjYW4ndCBhdXRvbWF0aWNhbGx5IGluc3RhbGwgdGhlIGNlcnRpZmljYXRlIChiZWNhdXNlXG4vLyBjZXJ0dXRpbCBpcyBub3QgYXZhaWxhYmxlIC8gaW5zdGFsbGFibGUpLCB3ZSBpbnN0ZWFkIHN0YXJ0IGEgcXVpY2sgd2ViIHNlcnZlciBhbmQgaG9zdCBvdXJcbi8vIGNlcnRpZmljYXRlIGZpbGUuIFRoZW4gd2Ugb3BlbiB0aGUgaG9zdGVkIGNlcnQgVVJMIGluIEZpcmVmb3ggdG8ga2ljayBvZmYgdGhlIEdVSSBmbG93LlxuYXN5bmMgZnVuY3Rpb24gb3BlbkNlcnRpZmljYXRlSW5GaXJlZm94KGZpcmVmb3hQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgZGVidWcoJ2FkZGluZyBkZXZlcnQgdG8gZmlyZWZveCBtYW51YWxseSAtIGxhdW5jaCB3ZWJzZXJ2ZXIgZm9yIGNlcnRpZmljYXRlIGhvc3RpbmcnKTtcbiAgbGV0IHBvcnQgPSBhd2FpdCBnZXRQb3J0KCk7XG4gIGxldCBzZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcigocmVxLCByZXMpID0+IHtcbiAgICByZXMud3JpdGVIZWFkKDIwMCwgeyAnQ29udGVudC10eXBlJzogJ2FwcGxpY2F0aW9uL3gteDUwOS1jYS1jZXJ0JyB9KTtcbiAgICByZXMud3JpdGUocmVhZEZpbGVTeW5jKHJvb3RDZXJ0UGF0aCkpO1xuICAgIHJlcy5lbmQoKTtcbiAgfSkubGlzdGVuKHBvcnQpO1xuICBkZWJ1ZygnY2VydGlmaWNhdGUgaXMgaG9zdGVkLCBzdGFydGluZyBmaXJlZm94IGF0IGhvc3RlZCBVUkwnKTtcbiAgY29uc29sZS5sb2coYFVuYWJsZSB0byBhdXRvbWF0aWNhbGx5IGluc3RhbGwgU1NMIGNlcnRpZmljYXRlIC0gcGxlYXNlIGZvbGxvdyB0aGUgcHJvbXB0cyBhdCBodHRwOi8vbG9jYWxob3N0OiR7IHBvcnQgfSBpbiBGaXJlZm94IHRvIHRydXN0IHRoZSByb290IGNlcnRpZmljYXRlYCk7XG4gIGNvbnNvbGUubG9nKCdTZWUgaHR0cHM6Ly9naXRodWIuY29tL2RhdmV3YXNtZXIvZGV2Y2VydCNob3ctaXQtd29ya3MgZm9yIG1vcmUgZGV0YWlscycpO1xuICBjb25zb2xlLmxvZygnLS0gUHJlc3MgPEVudGVyPiBvbmNlIHlvdSBmaW5pc2ggdGhlIEZpcmVmb3ggcHJvbXB0cyAtLScpO1xuICBleGVjKGAkeyBmaXJlZm94UGF0aCB9IGh0dHA6Ly9sb2NhbGhvc3Q6JHsgcG9ydCB9YCk7XG4gIGF3YWl0IHdhaXRGb3JVc2VyKCk7XG59XG5cbi8vIFRyeSB0byBpbnN0YWxsIGNlcnR1dGlsIGlmIGl0J3Mgbm90IGFscmVhZHkgYXZhaWxhYmxlLCBhbmQgcmV0dXJuIHRoZSBwYXRoIHRvIHRoZSBleGVjdXRhYmxlXG5mdW5jdGlvbiBsb29rdXBPckluc3RhbGxDZXJ0dXRpbChpbnN0YWxsQ2VydHV0aWw6IGJvb2xlYW4pOiBib29sZWFuIHwgc3RyaW5nIHtcbiAgZGVidWcoJ2xvb2tpbmcgZm9yIG5zcyB0b29saW5nIC4uLicpXG4gIGlmIChpc01hYykge1xuICAgIGRlYnVnKCdvbiBtYWMsIGxvb2tpbmcgZm9yIGhvbWVicmV3ICh0aGUgb25seSBtZXRob2QgdG8gaW5zdGFsbCBuc3MgdGhhdCBpcyBjdXJyZW50bHkgc3VwcG9ydGVkIGJ5IGRldmNlcnQnKTtcbiAgICBpZiAoY29tbWFuZEV4aXN0cygnYnJldycpKSB7XG4gICAgICBsZXQgbnNzUGF0aDogc3RyaW5nO1xuICAgICAgbGV0IGNlcnR1dGlsUGF0aDogc3RyaW5nO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY2VydHV0aWxQYXRoID0gcGF0aC5qb2luKHJ1bignYnJldyAtLXByZWZpeCBuc3MnKS50b1N0cmluZygpLnRyaW0oKSwgJ2JpbicsICdjZXJ0dXRpbCcpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBkZWJ1ZygnYnJldyB3YXMgZm91bmQsIGJ1dCBuc3MgaXMgbm90IGluc3RhbGxlZCcpO1xuICAgICAgICBpZiAoaW5zdGFsbENlcnR1dGlsKSB7XG4gICAgICAgICAgZGVidWcoJ2F0dGVtcHRpbmcgdG8gaW5zdGFsbCBuc3MgdmlhIGJyZXcnKTtcbiAgICAgICAgICBydW4oJ2JyZXcgaW5zdGFsbCBuc3MnKTtcbiAgICAgICAgICBjZXJ0dXRpbFBhdGggPSBwYXRoLmpvaW4ocnVuKCdicmV3IC0tcHJlZml4IG5zcycpLnRvU3RyaW5nKCkudHJpbSgpLCAnYmluJywgJ2NlcnR1dGlsJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBkZWJ1ZyhgRm91bmQgbnNzIGluc3RhbGxlZCBhdCAkeyBjZXJ0dXRpbFBhdGggfWApO1xuICAgICAgcmV0dXJuIGNlcnR1dGlsUGF0aDtcbiAgICB9XG4gIH0gZWxzZSBpZiAoaXNMaW51eCkge1xuICAgIGRlYnVnKCdvbiBsaW51eCwgY2hlY2tpbmcgaXMgbnNzIGlzIGFscmVhZHkgaW5zdGFsbGVkJyk7XG4gICAgaWYgKCFjb21tYW5kRXhpc3RzKCdjZXJ0dXRpbCcpKSB7XG4gICAgICBpZiAoaW5zdGFsbENlcnR1dGlsKSB7XG4gICAgICAgIGRlYnVnKCdub3QgYWxyZWFkeSBpbnN0YWxsZWQsIGluc3RhbGxpbmcgaXQgb3Vyc2VsdmVzJyk7XG4gICAgICAgIHJ1bignc3VkbyBhcHQgaW5zdGFsbCBsaWJuc3MzLXRvb2xzJyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkZWJ1Zygnbm90IGluc3RhbGxlZCBhbmQgZG8gbm90IHdhbnQgdG8gaW5zdGFsbCcpO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICAgIGRlYnVnKCdsb29rcyBsaWtlIG5zcyBpcyBpbnN0YWxsZWQnKTtcbiAgICByZXR1cm4gcnVuKCd3aGljaCBjZXJ0dXRpbCcpLnRvU3RyaW5nKCkudHJpbSgpO1xuICB9XG4gIC8vIFdpbmRvd3M/IEhhIVxuICByZXR1cm4gZmFsc2U7XG59Il19 |
\ | No newline at end of file |