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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9vdC1hdXRob3JpdHkuanMiLCJzb3VyY2VSb290IjoiL1VzZXJzL2phc29ubWlsbGVyL1Byb2plY3RzL2l0YWx5L2RldmNlcnQvIiwic291cmNlcyI6WyJyb290LWF1dGhvcml0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyQkFBNkQ7QUFDN0QsaURBQXFDO0FBQ3JDLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0Isb0NBQW9DO0FBQ3BDLHFDQUFxQztBQUNyQyxtREFBdUQ7QUFDdkQsNkJBQTZCO0FBQzdCLDJCQUEyQjtBQUUzQiwyQ0FTcUI7QUFDckIsbUNBS2lCO0FBRWpCLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVyQyxrR0FBa0c7QUFDbEcsa0dBQWtHO0FBQ2xHLHFDQUEwRCxlQUF3Qjs7UUFDaEYsS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7UUFDMUMsd0JBQXdCLEVBQUUsQ0FBQztRQUUzQixLQUFLLENBQUMsMkNBQTJDLENBQUMsQ0FBQztRQUNuRCxtQkFBVyxDQUFDLHVCQUFXLENBQUMsQ0FBQztRQUV6QixLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQztRQUMzRCxlQUFPLENBQUMsZUFBZ0IsMkJBQWdCLFNBQVUsdUJBQVksU0FBVSx3QkFBYSw4REFBOEQsQ0FBQyxDQUFDO1FBRXJKLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxDQUFBO1FBQzFELEVBQUUsQ0FBQyxDQUFDLGlCQUFLLENBQUMsQ0FBQyxDQUFDO1lBQ1YsTUFBTSxtQkFBbUIsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUM3QyxDQUFDO1FBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLG1CQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ25CLE1BQU0scUJBQXFCLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUFDLElBQUksQ0FBQyxDQUFDO1lBQ04sTUFBTSx1QkFBdUIsRUFBRSxDQUFDO1FBQ2xDLENBQUM7SUFDSCxD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QUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDakQsQ0FBQztJQUNELGVBQWU7SUFDZixNQUFNLENBQUMsS0FBSyxDQUFDO0FBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYywgZXhpc3RzU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCB7IGV4ZWMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCAqIGFzIGh0dHAgZnJvbSAnaHR0cCc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgZ2V0UG9ydCBmcm9tICdnZXQtcG9ydCc7XG5pbXBvcnQgKiBhcyBjcmVhdGVEZWJ1ZyBmcm9tICdkZWJ1Zyc7XG5pbXBvcnQgeyBzeW5jIGFzIGNvbW1hbmRFeGlzdHMgfSBmcm9tICdjb21tYW5kLWV4aXN0cyc7XG5pbXBvcnQgKiBhcyBnbG9iIGZyb20gJ2dsb2InO1xuaW1wb3J0ICogYXMgZW9sIGZyb20gJ2VvbCc7XG5cbmltcG9ydCB7XG4gIGlzTWFjLFxuICBpc0xpbnV4LFxuICBpc1dpbmRvd3MsXG4gIGNvbmZpZ1BhdGgsXG4gIHJvb3RLZXlQYXRoLFxuICByb290Q2VydFBhdGgsXG4gIG9wZW5zc2xDb25mUGF0aCxcbiAgb3BlbnNzbENvbmZUZW1wbGF0ZVxufSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQge1xuICBvcGVuc3NsLFxuICBnZW5lcmF0ZUtleSxcbiAgcnVuLFxuICB3YWl0Rm9yVXNlclxufSBmcm9tICcuL3V0aWxzJztcblxuY29uc3QgZGVidWcgPSBjcmVhdGVEZWJ1ZygnZGV2Y2VydCcpO1xuXG4vLyBJbnN0YWxsIHRoZSBvbmNlLXBlci1tYWNoaW5lIHRydXN0ZWQgcm9vdCBDQS4gV2UnbGwgdXNlIHRoaXMgQ0EgdG8gc2lnbiBwZXItYXBwIGNlcnRzLCBhbGxvd2luZ1xuLy8gdXMgdG8gbWluaW1pemUgdGhlIG5lZWQgZm9yIGVsZXZhdGVkIHBlcm1pc3Npb25zIHdoaWxlIHN0aWxsIGFsbG93aW5nIGZvciBwZXItYXBwIGNlcnRpZmljYXRlcy5cbmV4cG9ydCBkZWZhdWx0IGFzeW5jIGZ1bmN0aW9uIGluc3RhbGxDZXJ0aWZpY2F0ZUF1dGhvcml0eShpbnN0YWxsQ2VydHV0aWw6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgZGVidWcoYGdlbmVyYXRpbmcgb3BlbnNzbCBjb25maWd1cmF0aW9uYCk7XG4gIGdlbmVyYXRlT3BlblNTTENvbmZGaWxlcygpO1xuXG4gIGRlYnVnKGBnZW5lcmF0aW5nIHJvb3QgY2VydGlmaWNhdGUgYXV0aG9yaXR5IGtleWApO1xuICBnZW5lcmF0ZUtleShyb290S2V5UGF0aCk7XG5cbiAgZGVidWcoYGdlbmVyYXRpbmcgcm9vdCBjZXJ0aWZpY2F0ZSBhdXRob3JpdHkgY2VydGlmaWNhdGVgKTtcbiAgb3BlbnNzbChgcmVxIC1jb25maWcgJHsgb3BlbnNzbENvbmZQYXRoIH0gLWtleSAkeyByb290S2V5UGF0aCB9IC1vdXQgJHsgcm9vdENlcnRQYXRoIH0gLW5ldyAtc3ViaiBcIi9DTj1kZXZjZXJ0XCIgLXg1MDkgLWRheXMgNzAwMCAtZXh0ZW5zaW9ucyB2M19jYWApO1xuXG4gIGRlYnVnKGBhZGRpbmcgcm9vdCBjZXJ0aWZpY2F0ZSBhdXRob3JpdHkgdG8gdHJ1c3Qgc3RvcmVzYClcbiAgaWYgKGlzTWFjKSB7XG4gICAgYXdhaXQgYWRkVG9NYWNUcnVzdFN0b3JlcyhpbnN0YWxsQ2VydHV0aWwpO1xuICB9IGVsc2UgaWYgKGlzTGludXgpIHtcbiAgICBhd2FpdCBhZGRUb0xpbnV4VHJ1c3RTdG9yZXMoaW5zdGFsbENlcnR1dGlsKTtcbiAgfSBlbHNlIHtcbiAgICBhd2FpdCBhZGRUb1dpbmRvd3NUcnVzdFN0b3JlcygpO1xuICB9XG59XG5cbi8vIENvcHkgb3VyIG9wZW5zc2wgY29uZiB0ZW1wbGF0ZSB0byB0aGUgbG9jYWwgY29uZmlnIGZvbGRlciwgYW5kIHVwZGF0ZSB0aGUgcGF0aHMgdG8gYmUgT1Ncbi8vIHNwZWNpZmljLiBBbHNvIGluaXRpYWxpemVzIHRoZSBmaWxlcyBvcGVuc3NsIG5lZWRzIHRvIHNpZ24gY2VydGlmaWNhdGVzIGFzIGEgY2VydGlmaWNhdGVcbi8vIGF1dGhvcml0eVxuZnVuY3Rpb24gZ2VuZXJhdGVPcGVuU1NMQ29uZkZpbGVzKCkge1xuICBsZXQgY29uZlRlbXBsYXRlID0gcmVhZEZpbGVTeW5jKG9wZW5zc2xDb25mVGVtcGxhdGUsICd1dGYtOCcpO1xuICBjb25mVGVtcGxhdGUgPSBjb25mVGVtcGxhdGUucmVwbGFjZSgvREFUQUJBU0VfUEFUSC8sIGNvbmZpZ1BhdGgoJ2luZGV4LnR4dCcpLnJlcGxhY2UoL1xcXFwvZywgJ1xcXFxcXFxcJykpO1xuICBjb25mVGVtcGxhdGUgPSBjb25mVGVtcGxhdGUucmVwbGFjZSgvU0VSSUFMX1BBVEgvLCBjb25maWdQYXRoKCdzZXJpYWwnKS5yZXBsYWNlKC9cXFxcL2csICdcXFxcXFxcXCcpKTtcbiAgY29uZlRlbXBsYXRlID0gZW9sLmF1dG8oY29uZlRlbXBsYXRlKTtcbiAgd3JpdGVGaWxlU3luYyhvcGVuc3NsQ29uZlBhdGgsIGNvbmZUZW1wbGF0ZSk7XG4gIHdyaXRlRmlsZVN5bmMoY29uZmlnUGF0aCgnaW5kZXgudHh0JyksICcnKTtcbiAgd3JpdGVGaWxlU3luYyhjb25maWdQYXRoKCdzZXJpYWwnKSwgJzAxJyk7XG4gIC8vIFRoaXMgdmVyc2lvbiBudW1iZXIgbGV0cyB1cyB3cml0ZSBjb2RlIGluIHRoZSBmdXR1cmUgdGhhdCBpbnRlbGxpZ2VudGx5IHVwZ3JhZGVzIGFuIGV4aXN0aW5nXG4gIC8vIGRldmNlcnQgaW5zdGFsbGF0aW9uLiBUaGlzIFwiY2EtdmVyc2lvblwiIGlzIGluZGVwZW5kZW50IG9mIHRoZSBkZXZjZXJ0IHBhY2thZ2UgdmVyc2lvbiwgYW5kXG4gIC8vIHRyYWNrcyBjaGFuZ2VzIHRvIHRoZSByb290IGNlcnRpZmljYXRlIHNldHVwIG9ubHkuXG4gIHdyaXRlRmlsZVN5bmMoY29uZmlnUGF0aCgnZGV2Y2VydC1jYS12ZXJzaW9uJyksICcxJyk7XG59XG5cbi8vIG1hY09TIGlzIHByZXR0eSBzaW1wbGUgLSBqdXN0IGFkZCB0aGUgY2VydGlmaWNhdGUgdG8gdGhlIHN5c3RlbSBrZXljaGFpbiwgYW5kIG1vc3QgYXBwbGljYXRpb25zXG4vLyB3aWxsIGRlbGVnYXRlIHRvIHRoYXQgZm9yIGRldGVybWluaW5nIHRydXN0ZWQgY2VydGlmaWNhdGVzLiBGaXJlZm94LCBvZiBjb3Vyc2UsIGRvZXMgaXQncyBvd25cbi8vIHRoaW5nLiBXZSBjYW4gdHJ5IHRvIGF1dG9tYXRpY2FsbHkgaW5zdGFsbCB0aGUgY2VydCB3aXRoIEZpcmVmb3ggaWYgd2UgY2FuIHVzZSBjZXJ0dXRpbCB2aWEgdGhlXG4vLyBgbnNzYCBIb21lYnJldyBwYWNrYWdlLCBvdGhlcndpc2Ugd2UgZ28gbWFudWFsIHdpdGggdXNlci1mYWNpbmcgcHJvbXB0cy5cbmFzeW5jIGZ1bmN0aW9uIGFkZFRvTWFjVHJ1c3RTdG9yZXMoaW5zdGFsbENlcnR1dGlsOiBib29sZWFuKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIENocm9tZSwgU2FmYXJpLCBzeXN0ZW0gdXRpbHNcbiAgZGVidWcoJ2FkZGluZyBkZXZjZXJ0IHJvb3QgQ0EgdG8gbWFjT1Mgc3lzdGVtIGtleWNoYWluJyk7XG4gIHJ1bihgc3VkbyBzZWN1cml0eSBhZGQtdHJ1c3RlZC1jZXJ0IC1kIC1yIHRydXN0Um9vdCAtayAvTGlicmFyeS9LZXljaGFpbnMvU3lzdGVtLmtleWNoYWluIC1wIHNzbCAtcCBiYXNpYyBcIiR7IHJvb3RDZXJ0UGF0aCB9XCJgKTtcbiAgLy8gRmlyZWZveFxuICB0cnkge1xuICAgIC8vIFRyeSB0byB1c2UgY2VydHV0aWwgdG8gaW5zdGFsbCB0aGUgY2VydCBhdXRvbWF0aWNhbGx5XG4gICAgZGVidWcoJ2FkZGluZyBkZXZjZXJ0IHJvb3QgQ0EgdG8gZmlyZWZveCcpO1xuICAgIGF3YWl0IGFkZENlcnRpZmljYXRlVG9OU1NDZXJ0REIocGF0aC5qb2luKHByb2Nlc3MuZW52LkhPTUUsICdMaWJyYXJ5L0FwcGxpY2F0aW9uIFN1cHBvcnQvRmlyZWZveC9Qcm9maWxlcy8qJyksIHtcbiAgICAgIGluc3RhbGxDZXJ0dXRpbCxcbiAgICAgIGNoZWNrRm9yT3BlbkZpcmVmb3g6IHRydWVcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIE90aGVyd2lzZSwgb3BlbiB0aGUgY2VydCBpbiBGaXJlZm94IHRvIGluc3RhbGwgaXRcbiAgICBhd2FpdCBvcGVuQ2VydGlmaWNhdGVJbkZpcmVmb3goJy9BcHBsaWNhdGlvbnMvRmlyZWZveC5hcHAvQ29udGVudHMvTWFjT1MvZmlyZWZveCcpO1xuICB9XG59XG5cbi8vIExpbnV4IGlzIHN1cnByaXNpbmdseSBkaWZmaWN1bHQuIFRoZXJlIHNlZW1zIHRvIGJlIG11bHRpcGxlIHN5c3RlbS13aWRlIHJlcG9zaXRvcmllcyBmb3IgY2VydHMsXG4vLyBzbyB3ZSBjb3B5IG91cnMgdG8gZWFjaC4gSG93ZXZlciwgRmlyZWZveCBkb2VzIGl0J3MgdXN1YWwgc2VwYXJhdGUgdHJ1c3Qgc3RvcmUuIFBsdXMgQ2hyb21lXG4vLyByZWxpZXMgb24gdGhlIE5TUyB0b29saW5nIChsaWtlIEZpcmVmb3gpLCBidXQgdXNlcyB0aGUgdXNlcidzIE5TUyBkYXRhYmFzZSwgdW5saWtlIEZpcmVmb3ggd2hpY2hcbi8vIHVzZXMgYSBzZXBhcmF0ZSBNb3ppbGxhIG9uZS4gQW5kIHNpbmNlIENocm9tZSBkb2Vzbid0IHByb21wdCB0aGUgdXNlciB3aXRoIGEgR1VJIGZsb3cgd2hlblxuLy8gb3BlbmluZyBjZXJ0cywgaWYgd2UgY2FuJ3QgdXNlIGNlcnR1dGlsLCB3ZSdyZSBvdXQgb2YgbHVjay5cbmFzeW5jIGZ1bmN0aW9uIGFkZFRvTGludXhUcnVzdFN0b3JlcyhpbnN0YWxsQ2VydHV0aWw6IGJvb2xlYW4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gc3lzdGVtIHV0aWxzXG4gIGRlYnVnKCdhZGRpbmcgZGV2Y2VydCByb290IENBIHRvIGxpbnV4IHN5c3RlbS13aWRlIGNlcnRpZmljYXRlcycpO1xuICBydW4oYHN1ZG8gY3AgJHsgcm9vdENlcnRQYXRoIH0gL2V0Yy9zc2wvY2VydHMvZGV2Y2VydC5wZW1gKTtcbiAgcnVuKGBzdWRvIGNwICR7IHJvb3RDZXJ0UGF0aCB9IC91c3IvbG9jYWwvc2hhcmUvY2EtY2VydGlmaWNhdGVzL2RldmNlcnQuY2VyYCk7XG4gIHJ1bihgc3VkbyB1cGRhdGUtY2EtY2VydGlmaWNhdGVzYCk7XG4gIC8vIEZpcmVmb3hcbiAgdHJ5IHtcbiAgICAvLyBUcnkgdG8gdXNlIGNlcnR1dGlsIHRvIGluc3RhbGwgdGhlIGNlcnQgYXV0b21hdGljYWxseVxuICAgIGRlYnVnKCdhZGRpbmcgZGV2Y2VydCByb290IENBIHRvIGZpcmVmb3gnKTtcbiAgICBhd2FpdCBhZGRDZXJ0aWZpY2F0ZVRvTlNTQ2VydERCKHBhdGguam9pbihwcm9jZXNzLmVudi5IT01FLCAnLm1vemlsbGEvZmlyZWZveC8qJyksIHtcbiAgICAgIGluc3RhbGxDZXJ0dXRpbCxcbiAgICAgIGNoZWNrRm9yT3BlbkZpcmVmb3g6IHRydWVcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIE90aGVyd2lzZSwgb3BlbiB0aGUgY2VydCBpbiBGaXJlZm94IHRvIGluc3RhbGwgaXRcbiAgICBhd2FpdCBvcGVuQ2VydGlmaWNhdGVJbkZpcmVmb3goJ2ZpcmVmb3gnKTtcbiAgfVxuICAvLyBDaHJvbWVcbiAgdHJ5IHtcbiAgICBkZWJ1ZygnYWRkaW5nIGRldmNlcnQgcm9vdCBDQSB0byBjaHJvbWUnKTtcbiAgICBhd2FpdCBhZGRDZXJ0aWZpY2F0ZVRvTlNTQ2VydERCKHBhdGguam9pbihwcm9jZXNzLmVudi5IT01FLCAnLnBraS9uc3NkYicpLCB7IGluc3RhbGxDZXJ0dXRpbCB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUud2FybihgXG5XQVJOSU5HOiBCZWNhdXNlIHlvdSBkaWQgbm90IHBhc3MgaW4gXFxgaW5zdGFsbENlcnR1dGlsOiB0cnVlXFxgIHRvIGRldmNlcnQsIHdlXG5hcmUgdW5hYmxlIHRvIHVwZGF0ZSBDaHJvbWUgdG8gYXV0b21hdGljYWxseSB0cnVzdCBnZW5lcmF0ZWQgZGV2ZWxvcG1lbnRcbmNlcnRpZmljYXRlcy4gVGhlIGNlcnRpZmljYXRlcyB3aWxsIHdvcmssIGJ1dCBDaHJvbWUgd2lsbCBjb250aW51ZSB0byB3YXJuIHlvdVxudGhhdCB0aGV5IGFyZSB1bnRydXN0ZWQuYCk7XG4gIH1cbn1cblxuLy8gV2luZG93cyBpcyBhdCBsZWFzdCBzaW1wbGUuIExpa2UgbWFjT1MsIG1vc3QgYXBwbGljYXRpb25zIHdpbGwgZGVsZWdhdGUgdG8gdGhlIHN5c3RlbSB0cnVzdFxuLy8gc3RvcmUsIHdoaWNoIGlzIHVwZGF0ZWQgd2l0aCB0aGUgY29uZnVzaW5nbHkgbmFtZWQgYGNlcnR1dGlsYCBleGUgKG5vdCB0aGUgc2FtZSBhcyB0aGVcbi8vIE5TUy9Nb3ppbGxhIGNlcnR1dGlsKS4gRmlyZWZveCBkb2VzIGl0J3Mgb3duIHRoaW5nIGFzIHVzdWFsLCBhbmQgZ2V0dGluZyBhIGNvcHkgb2YgTlNTIGNlcnR1dGlsXG4vLyBvbnRvIHRoZSBXaW5kb3dzIG1hY2hpbmUgdG8gdHJ5IHVwZGF0aW5nIHRoZSBGaXJlZm94IHN0b3JlIGlzIGJhc2ljYWxseSBhIG5pZ2h0bWFyZSwgc28gd2UgZG9uJ3Rcbi8vIGV2ZW4gdHJ5IGl0IC0gd2UganVzdCBiYWlsIG91dCB0byB0aGUgR1VJLlxuYXN5bmMgZnVuY3Rpb24gYWRkVG9XaW5kb3dzVHJ1c3RTdG9yZXMoKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIElFLCBDaHJvbWUsIHN5c3RlbSB1dGlsc1xuICBkZWJ1ZygnYWRkaW5nIGRldmNlcnQgcm9vdCB0byBXaW5kb3dzIE9TIHRydXN0IHN0b3JlJylcbiAgcnVuKGBjZXJ0dXRpbCAtYWRkc3RvcmUgLXVzZXIgcm9vdCAkeyByb290Q2VydFBhdGggfWApO1xuICAvLyBGaXJlZm94IChkb24ndCBldmVuIHRyeSBOU1MgY2VydHV0aWwsIG5vIGVhc3kgaW5zdGFsbCBmb3IgV2luZG93cylcbiAgYXdhaXQgb3BlbkNlcnRpZmljYXRlSW5GaXJlZm94KCdzdGFydCBmaXJlZm94Jyk7XG59XG5cbi8vIEdpdmVuIGEgZGlyZWN0b3J5IG9yIGdsb2IgcGF0dGVybiBvZiBkaXJlY3RvcmllcywgYXR0ZW1wdCB0byBpbnN0YWxsIHRoZSBjZXJ0aWZpY2F0ZSB0byBlYWNoXG4vLyBkaXJlY3RvcnkgY29udGFpbmluZyBhbiBOU1MgZGF0YWJhc2UuXG5hc3luYyBmdW5jdGlvbiBhZGRDZXJ0aWZpY2F0ZVRvTlNTQ2VydERCKG5zc0Rpckdsb2I6IHN0cmluZywgb3B0aW9uczogeyBpbnN0YWxsQ2VydHV0aWw/OiBib29sZWFuLCBjaGVja0Zvck9wZW5GaXJlZm94PzogYm9vbGVhbiB9ID0ge30pOiBQcm9taXNlPHZvaWQ+IHtcbiAgbGV0IGNlcnR1dGlsUGF0aCA9IGxvb2t1cE9ySW5zdGFsbENlcnR1dGlsKG9wdGlvbnMuaW5zdGFsbENlcnR1dGlsKTtcbiAgaWYgKCFjZXJ0dXRpbFBhdGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NlcnR1dGlsIG5vdCBhdmFpbGFibGUsIGFuZCBgaW5zdGFsbENlcnR1dGlsYCB3YXMgZmFsc2UnKTtcbiAgfVxuICAvLyBGaXJlZm94IGFwcGVhcnMgdG8gbG9hZCB0aGUgTlNTIGRhdGFiYXNlIGluLW1lbW9yeSBvbiBzdGFydHVwLCBhbmQgb3ZlcndyaXRlIG9uIGV4aXQuIFNvIHdlXG4gIC8vIGhhdmUgdG8gYXNrIHRoZSB1c2VyIHRvIHF1aXRlIEZpcmVmb3ggZmlyc3Qgc28gb3VyIGNoYW5nZXMgZG9uJ3QgZ2V0IG92ZXJ3cml0dGVuLlxuICBpZiAob3B0aW9ucy5jaGVja0Zvck9wZW5GaXJlZm94KSB7XG4gICAgbGV0IHJ1bm5pbmdQcm9jZXNzZXMgPSBydW4oJ3BzIGF1eCcpO1xuICAgIGlmIChydW5uaW5nUHJvY2Vzc2VzLmluZGV4T2YoJ2ZpcmVmb3gnKSA+IC0xKSB7XG4gICAgICBjb25zb2xlLmxvZygnUGxlYXNlIGNsb3NlIEZpcmVmb3ggYmVmb3JlIGNvbnRpbnVpbmcgKFByZXNzIDxFbnRlcj4gd2hlbiByZWFkeSknKTtcbiAgICAgIGF3YWl0IHdhaXRGb3JVc2VyKCk7XG4gICAgfVxuICB9XG4gIGRlYnVnKGB0cnlpbmcgdG8gaW5zdGFsbCBjZXJ0aWZpY2F0ZSBpbnRvIE5TUyBkYXRhYmFzZXMgaW4gJHsgbnNzRGlyR2xvYiB9YCk7XG4gIGdsb2Iuc3luYyhuc3NEaXJHbG9iKS5mb3JFYWNoKChwb3RlbnRpYWxOU1NEQkRpcikgPT4ge1xuICAgIGRlYnVnKGBjaGVja2luZyB0byBzZWUgaWYgJHsgcG90ZW50aWFsTlNTREJEaXIgfSBpcyBhIHZhbGlkIE5TUyBkYXRhYmFzZSBkaXJlY3RvcnlgKTtcbiAgICBpZiAoZXhpc3RzU3luYyhwYXRoLmpvaW4ocG90ZW50aWFsTlNTREJEaXIsICdjZXJ0OC5kYicpKSkge1xuICAgICAgZGVidWcoYEZvdW5kIGxlZ2FjeSBOU1MgZGF0YWJhc2UgaW4gJHsgcG90ZW50aWFsTlNTREJEaXIgfSwgYWRkaW5nIGRldmNlcnQgLi4uYClcbiAgICAgIHJ1bihgJHsgY2VydHV0aWxQYXRoIH0gLUEgLWQgXCIkeyBwb3RlbnRpYWxOU1NEQkRpciB9XCIgLXQgJ0MsLCcgLWkgJHsgcm9vdENlcnRQYXRoIH0gLW4gZGV2Y2VydGApO1xuICAgIH0gZWxzZSBpZiAoZXhpc3RzU3luYyhwYXRoLmpvaW4ocG90ZW50aWFsTlNTREJEaXIsICdjZXJ0OS5kYicpKSkge1xuICAgICAgZGVidWcoYEZvdW5kIG1vZGVybiBOU1MgZGF0YWJhc2UgaW4gJHsgcG90ZW50aWFsTlNTREJEaXIgfSwgYWRkaW5nIGRldmNlcnQgLi4uYClcbiAgICAgIHJ1bihgJHsgY2VydHV0aWxQYXRoIH0gLUEgLWQgXCJzcWw6JHsgcG90ZW50aWFsTlNTREJEaXIgfVwiIC10ICdDLCwnIC1pICR7IHJvb3RDZXJ0UGF0aCB9IC1uIGRldmNlcnRgKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vLyBXaGVuIGEgRmlyZWZveCB0YWIgaXMgZGlyZWN0ZWQgdG8gYSBVUkwgdGhhdCByZXR1cm5zIGEgY2VydGlmaWNhdGUsIGl0IHdpbGwgYXV0b21hdGljYWxseSBwcm9tcHRcbi8vIHRoZSB1c2VyIGlmIHRoZXkgd2FudCB0byBhZGQgaXQgdG8gdGhlaXIgdHJ1c3RlZCBjZXJ0aWZpY2F0ZXMuIFRoaXMgaXMgaGFuZHkgc2luY2UgRmlyZWZveCBpcyBieVxuLy8gZmFyIHRoZSBtb3N0IHRyb3VibGVzb21lIHRvIGhhbmRsZS4gSWYgd2UgY2FuJ3QgYXV0b21hdGljYWxseSBpbnN0YWxsIHRoZSBjZXJ0aWZpY2F0ZSAoYmVjYXVzZVxuLy8gY2VydHV0aWwgaXMgbm90IGF2YWlsYWJsZSAvIGluc3RhbGxhYmxlKSwgd2UgaW5zdGVhZCBzdGFydCBhIHF1aWNrIHdlYiBzZXJ2ZXIgYW5kIGhvc3Qgb3VyXG4vLyBjZXJ0aWZpY2F0ZSBmaWxlLiBUaGVuIHdlIG9wZW4gdGhlIGhvc3RlZCBjZXJ0IFVSTCBpbiBGaXJlZm94IHRvIGtpY2sgb2ZmIHRoZSBHVUkgZmxvdy5cbmFzeW5jIGZ1bmN0aW9uIG9wZW5DZXJ0aWZpY2F0ZUluRmlyZWZveChmaXJlZm94UGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGRlYnVnKCdhZGRpbmcgZGV2ZXJ0IHRvIGZpcmVmb3ggbWFudWFsbHkgLSBsYXVuY2ggd2Vic2VydmVyIGZvciBjZXJ0aWZpY2F0ZSBob3N0aW5nJyk7XG4gIGxldCBwb3J0ID0gYXdhaXQgZ2V0UG9ydCgpO1xuICBsZXQgc2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoKHJlcSwgcmVzKSA9PiB7XG4gICAgcmVzLndyaXRlSGVhZCgyMDAsIHsgJ0NvbnRlbnQtdHlwZSc6ICdhcHBsaWNhdGlvbi94LXg1MDktY2EtY2VydCcgfSk7XG4gICAgcmVzLndyaXRlKHJlYWRGaWxlU3luYyhyb290Q2VydFBhdGgpKTtcbiAgICByZXMuZW5kKCk7XG4gIH0pLmxpc3Rlbihwb3J0KTtcbiAgZGVidWcoJ2NlcnRpZmljYXRlIGlzIGhvc3RlZCwgc3RhcnRpbmcgZmlyZWZveCBhdCBob3N0ZWQgVVJMJyk7XG4gIGNvbnNvbGUubG9nKGBVbmFibGUgdG8gYXV0b21hdGljYWxseSBpbnN0YWxsIFNTTCBjZXJ0aWZpY2F0ZSAtIHBsZWFzZSBmb2xsb3cgdGhlIHByb21wdHMgYXQgaHR0cDovL2xvY2FsaG9zdDokeyBwb3J0IH0gaW4gRmlyZWZveCB0byB0cnVzdCB0aGUgcm9vdCBjZXJ0aWZpY2F0ZWApO1xuICBjb25zb2xlLmxvZygnU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9kYXZld2FzbWVyL2RldmNlcnQjaG93LWl0LXdvcmtzIGZvciBtb3JlIGRldGFpbHMnKTtcbiAgY29uc29sZS5sb2coJy0tIFByZXNzIDxFbnRlcj4gb25jZSB5b3UgZmluaXNoIHRoZSBGaXJlZm94IHByb21wdHMgLS0nKTtcbiAgZXhlYyhgJHsgZmlyZWZveFBhdGggfSBodHRwOi8vbG9jYWxob3N0OiR7IHBvcnQgfWApO1xuICBhd2FpdCB3YWl0Rm9yVXNlcigpO1xufVxuXG4vLyBUcnkgdG8gaW5zdGFsbCBjZXJ0dXRpbCBpZiBpdCdzIG5vdCBhbHJlYWR5IGF2YWlsYWJsZSwgYW5kIHJldHVybiB0aGUgcGF0aCB0byB0aGUgZXhlY3V0YWJsZVxuZnVuY3Rpb24gbG9va3VwT3JJbnN0YWxsQ2VydHV0aWwoaW5zdGFsbENlcnR1dGlsOiBib29sZWFuKTogYm9vbGVhbiB8IHN0cmluZyB7XG4gIGRlYnVnKCdsb29raW5nIGZvciBuc3MgdG9vbGluZyAuLi4nKVxuICBpZiAoaXNNYWMpIHtcbiAgICBkZWJ1Zygnb24gbWFjLCBsb29raW5nIGZvciBob21lYnJldyAodGhlIG9ubHkgbWV0aG9kIHRvIGluc3RhbGwgbnNzIHRoYXQgaXMgY3VycmVudGx5IHN1cHBvcnRlZCBieSBkZXZjZXJ0Jyk7XG4gICAgaWYgKGNvbW1hbmRFeGlzdHMoJ2JyZXcnKSkge1xuICAgICAgbGV0IG5zc1BhdGg6IHN0cmluZztcbiAgICAgIGxldCBjZXJ0dXRpbFBhdGg6IHN0cmluZztcbiAgICAgIHRyeSB7XG4gICAgICAgIGNlcnR1dGlsUGF0aCA9IHBhdGguam9pbihydW4oJ2JyZXcgLS1wcmVmaXggbnNzJykudG9TdHJpbmcoKS50cmltKCksICdiaW4nLCAnY2VydHV0aWwnKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgZGVidWcoJ2JyZXcgd2FzIGZvdW5kLCBidXQgbnNzIGlzIG5vdCBpbnN0YWxsZWQnKTtcbiAgICAgICAgaWYgKGluc3RhbGxDZXJ0dXRpbCkge1xuICAgICAgICAgIGRlYnVnKCdhdHRlbXB0aW5nIHRvIGluc3RhbGwgbnNzIHZpYSBicmV3Jyk7XG4gICAgICAgICAgcnVuKCdicmV3IGluc3RhbGwgbnNzJyk7XG4gICAgICAgICAgY2VydHV0aWxQYXRoID0gcGF0aC5qb2luKHJ1bignYnJldyAtLXByZWZpeCBuc3MnKS50b1N0cmluZygpLnRyaW0oKSwgJ2JpbicsICdjZXJ0dXRpbCcpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZGVidWcoYEZvdW5kIG5zcyBpbnN0YWxsZWQgYXQgJHsgY2VydHV0aWxQYXRoIH1gKTtcbiAgICAgIHJldHVybiBjZXJ0dXRpbFBhdGg7XG4gICAgfVxuICB9IGVsc2UgaWYgKGlzTGludXgpIHtcbiAgICBkZWJ1Zygnb24gbGludXgsIGNoZWNraW5nIGlzIG5zcyBpcyBhbHJlYWR5IGluc3RhbGxlZCcpO1xuICAgIGlmICghY29tbWFuZEV4aXN0cygnY2VydHV0aWwnKSkge1xuICAgICAgaWYgKGluc3RhbGxDZXJ0dXRpbCkge1xuICAgICAgICBkZWJ1Zygnbm90IGFscmVhZHkgaW5zdGFsbGVkLCBpbnN0YWxsaW5nIGl0IG91cnNlbHZlcycpO1xuICAgICAgICBydW4oJ3N1ZG8gYXB0IGluc3RhbGwgbGlibnNzMy10b29scycpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZGVidWcoJ25vdCBpbnN0YWxsZWQgYW5kIGRvIG5vdCB3YW50IHRvIGluc3RhbGwnKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgICBkZWJ1ZygnbG9va3MgbGlrZSBuc3MgaXMgaW5zdGFsbGVkJyk7XG4gICAgcmV0dXJuIHJ1bignd2hpY2ggY2VydHV0aWwnKS50b1N0cmluZygpLnRyaW0oKTtcbiAgfVxuICAvLyBXaW5kb3dzPyBIYSFcbiAgcmV0dXJuIGZhbHNlO1xufSJdfQ== |
\ | No newline at end of file |