sliftutils 1.7.125 → 1.7.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/bin/derivekey.js +10 -0
  2. package/bin/portsecuredaemon.js +13 -0
  3. package/bin/testnotify.js +9 -0
  4. package/bin/unrevoke.js +9 -0
  5. package/package.json +10 -3
  6. package/security/README.md +66 -2
  7. package/security/authorizedKeys/authorizedKeys.ts +68 -5
  8. package/security/authorizedKeys/daemon/authLog.ts +186 -0
  9. package/security/authorizedKeys/daemon/changes.ts +26 -0
  10. package/security/authorizedKeys/daemon/daemon.ts +335 -0
  11. package/security/authorizedKeys/daemon/git.ts +119 -0
  12. package/security/authorizedKeys/daemon/notify.ts +25 -0
  13. package/security/authorizedKeys/daemon/paths.ts +26 -0
  14. package/security/authorizedKeys/daemon/portsecure.service +3 -2
  15. package/security/authorizedKeys/daemon/revocation.ts +329 -0
  16. package/security/authorizedKeys/daemon/rootKeys.ts +198 -0
  17. package/security/authorizedKeys/daemon/sessions.ts +139 -0
  18. package/security/authorizedKeys/daemon/sshdConfig.ts +85 -0
  19. package/security/authorizedKeys/daemon/state.ts +122 -0
  20. package/security/authorizedKeys/daemon/trust.ts +291 -0
  21. package/security/authorizedKeys/daemon/userKeys.ts +76 -0
  22. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +111 -0
  23. package/security/authorizedKeys/dist/revokeSource.ts.cache +44 -0
  24. package/security/authorizedKeys/dist/secureSSH.ts.cache +539 -0
  25. package/security/authorizedKeys/dist/sources.ts.cache +24 -0
  26. package/security/authorizedKeys/dist/unrevoke.ts.cache +167 -0
  27. package/security/authorizedKeys/revokeSource.ts +40 -0
  28. package/security/authorizedKeys/secureSSH.ts +240 -38
  29. package/security/authorizedKeys/unrevoke.ts +175 -0
  30. package/security/helpers/dist/paths.ts.cache +28 -0
  31. package/security/helpers/dist/remoteSSH.ts.cache +90 -0
  32. package/security/helpers/dist/spawn.ts.cache +34 -0
  33. package/security/helpers/remoteSSH.ts +18 -6
  34. package/security/helpers/spawn.ts +5 -1
  35. package/security/keys/deriveKey.ts +72 -0
  36. package/security/keys/dist/deriveKey.ts.cache +72 -0
  37. package/security/keys/dist/sshKeyFile.ts.cache +153 -0
  38. package/security/keys/sshKeyFile.ts +156 -0
  39. package/security/notifications/discord.ts +18 -1
  40. package/security/notifications/dist/discord.ts.cache +197 -0
  41. package/security/notifications/setupNotify.ts +17 -8
  42. package/security/notifications/testNotify.ts +48 -0
  43. package/security/signedFiles/dist/manifest.ts.cache +85 -0
  44. package/security/signedFiles/dist/signFiles.ts.cache +181 -0
  45. package/security/signedFiles/manifest.ts +38 -8
  46. package/security/signedFiles/signFiles.ts +123 -49
  47. package/security/authorizedKeys/daemon/portsecureDaemon.js +0 -1032
@@ -0,0 +1,539 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
6
+ //exports.main = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const discord_1 = require("../notifications/discord");
11
+ const authorizedKeys_1 = require("./authorizedKeys");
12
+ const sources_1 = require("./sources");
13
+ const revokeSource_1 = require("./revokeSource");
14
+ const unrevoke_1 = require("./unrevoke");
15
+ const paths_1 = require("../helpers/paths");
16
+ const spawn_1 = require("../helpers/spawn");
17
+ const remoteSSH_1 = require("../helpers/remoteSSH");
18
+ const SERVICE_SOURCE = path_1.default.join(__dirname, "daemon", "portsecure.service");
19
+ // The daemon runs from a checkout of this repo on the host, rather than from a copy we upload, so
20
+ // a host updates itself from github the same way anything else does. sliftutils is public, so this
21
+ // needs no key.
22
+ const SLIFTUTILS_URL = "https://github.com/sliftist/sliftutils.git";
23
+ const REMOTE_CHECKOUT_PATH = "/opt/portsecure/sliftutils";
24
+ const REMOTE_SERVICE_PATH = "/etc/systemd/system/portsecure.service";
25
+ const REMOTE_CONFIG_PATH = "/etc/portsecure/daemon.json";
26
+ const ROOT_AUTHORIZED_KEYS = "/root/.ssh/authorized_keys";
27
+ const SERVICE_NAME = "portsecure";
28
+ const MAX_ERROR_BODY_LENGTH = 500;
29
+ const VERBS = ["add", "remove", "list", "update"];
30
+ // The repo url is optional, and defaults to the repo the command is run from.
31
+ const USAGE = `Usage:
32
+ yarn securessh <host> add <repo-private-key> [repo-url]
33
+ yarn securessh <host> remove [repo-url]
34
+ yarn securessh <host> list
35
+ yarn securessh <host> update`;
36
+ async function pathExists(filePath) {
37
+ try {
38
+ await promises_1.default.access(filePath);
39
+ return true;
40
+ }
41
+ catch (e) {
42
+ return false;
43
+ }
44
+ }
45
+ async function runLocal(config) {
46
+ let { command, args, cwd, allowFailure } = config;
47
+ let result = await (0, spawn_1.spawnPromise)({ command, args, cwd });
48
+ if (result.error) {
49
+ throw new Error(`Expected ${command} to run, failed with ${result.error.message}`);
50
+ }
51
+ if (result.status !== 0 && !allowFailure) {
52
+ throw new Error(`Expected ${command} ${args.join(" ")} to exit 0, was ${result.status}. `
53
+ + `${(result.stdout + result.stderr).trim().slice(0, MAX_ERROR_BODY_LENGTH)}`);
54
+ }
55
+ return result;
56
+ }
57
+ /** A private key cannot authenticate an https remote, so github urls are converted to the ssh form
58
+ the key can actually be used with. */
59
+ function normalizeRepoURL(repoURL) {
60
+ let httpsMatch = repoURL.match(/^https?:\/\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
61
+ if (!httpsMatch) {
62
+ return repoURL;
63
+ }
64
+ let [, host, repoPath] = httpsMatch;
65
+ return `git@${host}:${repoPath}.git`;
66
+ }
67
+ async function gitWithKey(config) {
68
+ let { keyPath, args, cwd, allowFailure } = config;
69
+ // core.sshCommand keeps key selection with the command instead of in the environment.
70
+ let sshCommand = `ssh -i ${keyPath} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new`;
71
+ let result = await (0, spawn_1.spawnPromise)({ command: "git", args: ["-c", `core.sshCommand=${sshCommand}`, ...args], cwd });
72
+ if (result.error) {
73
+ throw new Error(`Expected git to run, failed with ${result.error.message}`);
74
+ }
75
+ if (result.status !== 0 && !allowFailure) {
76
+ throw new Error(`Expected git ${args.join(" ")} to exit 0, was ${result.status}. `
77
+ + `${(result.stdout + result.stderr).trim().slice(0, MAX_ERROR_BODY_LENGTH)}`);
78
+ }
79
+ return result;
80
+ }
81
+ /** Asks ssh which key it actually authenticated with. This is the key that must survive the
82
+ daemon taking over authorized_keys, otherwise a deploy locks us out. */
83
+ async function findAuthenticatingFingerprint(host) {
84
+ let result = await (0, spawn_1.spawnPromise)({
85
+ command: "ssh",
86
+ args: ["-v", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10", host, "true"],
87
+ });
88
+ let verboseOutput = result.stderr || "";
89
+ if (result.status !== 0) {
90
+ throw new Error(`Expected to ssh into ${host}, failed. ${verboseOutput.slice(-MAX_ERROR_BODY_LENGTH)}`);
91
+ }
92
+ let acceptedMatch = verboseOutput.match(/Server accepts key:.*?(SHA256:[A-Za-z0-9+/=]+)/);
93
+ if (!acceptedMatch) {
94
+ throw new Error(`Expected ${host} to accept a public key, but the session did not authenticate with one.`
95
+ + ` portsecure disables password login, so key based access has to work first.`);
96
+ }
97
+ return acceptedMatch[1];
98
+ }
99
+ async function fingerprintKeys(keys) {
100
+ if (!keys.length) {
101
+ return [];
102
+ }
103
+ let temporaryDirectory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "portsecure-keys-"));
104
+ let keysPath = path_1.default.join(temporaryDirectory, "authorized_keys");
105
+ await promises_1.default.writeFile(keysPath, `${keys.join("\n")}\n`);
106
+ let result = await runLocal({ command: "ssh-keygen", args: ["-lf", keysPath], allowFailure: true });
107
+ await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
108
+ let fingerprints = [];
109
+ for (let line of result.stdout.split("\n")) {
110
+ let match = line.match(/(SHA256:[A-Za-z0-9+/=]+)/);
111
+ if (match) {
112
+ fingerprints.push(match[1]);
113
+ }
114
+ }
115
+ return fingerprints;
116
+ }
117
+ async function cloneRepoForInspection(config) {
118
+ let { repoURL, keyPath } = config;
119
+ let temporaryDirectory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "portsecure-repo-"));
120
+ let repoPath = path_1.default.join(temporaryDirectory, "repo");
121
+ await gitWithKey({ keyPath, args: ["clone", "--depth", "1", repoURL, repoPath] });
122
+ return repoPath;
123
+ }
124
+ /** With no repo url given, the repo we are standing in is used. It has to actually hold keys
125
+ before we hand it to a host, so a stray working directory cannot be deployed by accident. */
126
+ async function resolveRepoURL(passedURL) {
127
+ if (passedURL) {
128
+ return normalizeRepoURL(passedURL);
129
+ }
130
+ let topLevel = await runLocal({ command: "git", args: ["rev-parse", "--show-toplevel"], allowFailure: true });
131
+ if (topLevel.status !== 0) {
132
+ throw new Error(`Expected a repo url, or the current directory to be inside a git repo, it is not.\n${USAGE}`);
133
+ }
134
+ let repoPath = topLevel.stdout.trim();
135
+ try {
136
+ await (0, authorizedKeys_1.readRepoKeys)(repoPath);
137
+ }
138
+ catch (e) {
139
+ throw new Error(`Expected a repo url, or the current repo (${repoPath}) to hold keys, it does not.\n${e}\n${USAGE}`);
140
+ }
141
+ let origin = await runLocal({
142
+ command: "git",
143
+ args: ["remote", "get-url", "origin"],
144
+ cwd: repoPath,
145
+ allowFailure: true,
146
+ });
147
+ if (origin.status !== 0 || !origin.stdout.trim()) {
148
+ throw new Error(`Expected the current repo (${repoPath}) to have an origin remote, it has none.`
149
+ + ` The host clones the source itself, so a local path is no use to it.`);
150
+ }
151
+ let repoURL = normalizeRepoURL(origin.stdout.trim());
152
+ console.log(`No repo url given, using the current repo: ${repoURL}`);
153
+ return repoURL;
154
+ }
155
+ /** Every source's revoke repo has to exist and hold at least one commit, or the hosts using it
156
+ cannot record a revocation. Checked with this machine's own git credentials, since update is
157
+ not given any deploy key, and an empty repo is initialised rather than merely complained about.
158
+
159
+ A host that cannot write a revocation silently keeps accepting a key it just saw being misused,
160
+ which is the one failure this whole thing exists to prevent, so it is checked on every deploy
161
+ and not only when a source is first added. */
162
+ async function ensureRevokeReposExist(repoSources) {
163
+ for (let repoURL of repoSources) {
164
+ let revokeURL = (0, revokeSource_1.revokeRepoURL)(repoURL);
165
+ let temporaryDirectory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "portsecure-revoke-"));
166
+ let checkoutPath = path_1.default.join(temporaryDirectory, "repo");
167
+ let clone = await runLocal({ command: "git", args: ["clone", revokeURL, checkoutPath], allowFailure: true });
168
+ if (clone.status !== 0) {
169
+ await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
170
+ throw new Error(`Expected ${revokeURL} to exist, it does not, so ${repoURL} has nowhere to record a`
171
+ + ` revocation.\nCreate it, then run "yarn securessh <host> add" for that source to`
172
+ + ` register its deploy key.\n${(clone.stdout + clone.stderr).trim().slice(0, MAX_ERROR_BODY_LENGTH)}`);
173
+ }
174
+ let head = await runLocal({ command: "git", args: ["-C", checkoutPath, "rev-parse", "HEAD"], allowFailure: true });
175
+ if (head.status !== 0) {
176
+ console.log(`${revokeURL} is empty, giving it a first commit`);
177
+ await promises_1.default.writeFile(path_1.default.join(checkoutPath, "README.md"), `# revoked keys\n\nWritten by portsecure. Each file under revocations/ is one key that was used from an\n`
178
+ + `address it is not allowed from, and is no longer accepted anywhere.\n`);
179
+ for (let args of [
180
+ ["-C", checkoutPath, "add", "-A"],
181
+ ["-C", checkoutPath, "-c", "user.email=portsecure@localhost", "-c", "user.name=portsecure", "commit", "-m", "initialise revoke repo"],
182
+ ["-C", checkoutPath, "push", "origin", "HEAD"],
183
+ ]) {
184
+ await runLocal({ command: "git", args });
185
+ }
186
+ }
187
+ await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
188
+ console.log(`${revokeURL} is ready`);
189
+ }
190
+ }
191
+ /** The revoke repo has to exist and be writable before a host is set up, because a host that
192
+ cannot write a revocation cannot revoke a key that is being misused. The key for it is derived
193
+ from the source's, since github will not take one public key on two repos. */
194
+ async function ensureRevokeRepo(config) {
195
+ let { keyPath, repoURL } = config;
196
+ let revokeURL = (0, revokeSource_1.revokeRepoURL)(repoURL);
197
+ let derived = (0, revokeSource_1.deriveRevokeKey)(await promises_1.default.readFile(keyPath, "utf8"));
198
+ let temporaryDirectory = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "portsecure-revoke-"));
199
+ let derivedKeyPath = path_1.default.join(temporaryDirectory, "key");
200
+ await promises_1.default.writeFile(derivedKeyPath, derived.privateKeyFile, { mode: 0o600 });
201
+ let checkoutPath = path_1.default.join(temporaryDirectory, "repo");
202
+ let explain = (problem) => new Error(`${problem}\n`
203
+ + `Create ${revokeURL} and add this as a deploy key WITH WRITE ACCESS:\n`
204
+ + ` ${derived.publicKey} ${revokeSource_1.REVOKE_KEY_LABEL}\n`
205
+ + `It has to be this key: it is derived from ${keyPath}, and github will not accept the same`
206
+ + ` public key on two repositories.`);
207
+ console.log(`Checking ${revokeURL}`);
208
+ let clone = await gitWithKey({ keyPath: derivedKeyPath, args: ["clone", revokeURL, checkoutPath], allowFailure: true });
209
+ if (clone.status !== 0) {
210
+ throw explain(`Expected ${revokeURL} to be readable with the derived key, it is not.`);
211
+ }
212
+ // An empty repo has no branch for the daemon to clone, so it gets its first commit here. That
213
+ // doubles as the proof that we can write to it.
214
+ let head = await gitWithKey({ keyPath: derivedKeyPath, args: ["rev-parse", "HEAD"], cwd: checkoutPath, allowFailure: true });
215
+ if (head.status !== 0) {
216
+ await promises_1.default.writeFile(path_1.default.join(checkoutPath, "README.md"), `# revoked keys\n\nWritten by portsecure. Each file under revocations/ is one key that was used from an\n`
217
+ + `address it is not allowed from, and is no longer accepted anywhere.\n`);
218
+ for (let args of [
219
+ ["add", "-A"],
220
+ ["-c", "user.email=portsecure@localhost", "-c", "user.name=portsecure", "commit", "-m", "initialise revoke repo"],
221
+ ]) {
222
+ await gitWithKey({ keyPath: derivedKeyPath, args, cwd: checkoutPath });
223
+ }
224
+ let push = await gitWithKey({ keyPath: derivedKeyPath, args: ["push", "origin", "HEAD"], cwd: checkoutPath, allowFailure: true });
225
+ if (push.status !== 0) {
226
+ throw explain(`Expected write access to ${revokeURL}, the first push was refused.\n${(push.stdout + push.stderr).trim().slice(0, MAX_ERROR_BODY_LENGTH)}`);
227
+ }
228
+ }
229
+ else {
230
+ let dryRun = await gitWithKey({ keyPath: derivedKeyPath, args: ["push", "--dry-run", "origin", "HEAD"], cwd: checkoutPath, allowFailure: true });
231
+ if (dryRun.status !== 0) {
232
+ throw explain(`Expected write access to ${revokeURL}, a dry run push was refused.\n${(dryRun.stdout + dryRun.stderr).trim().slice(0, MAX_ERROR_BODY_LENGTH)}`);
233
+ }
234
+ }
235
+ await promises_1.default.rm(temporaryDirectory, { recursive: true, force: true });
236
+ console.log(`${revokeURL} is writable`);
237
+ }
238
+ async function readRemoteConfig(host) {
239
+ let contents = await (0, remoteSSH_1.readRemoteFile)({ host, filePath: REMOTE_CONFIG_PATH });
240
+ if (!contents) {
241
+ return { hostLabel: host, repoSources: [] };
242
+ }
243
+ let parsed = JSON.parse(contents);
244
+ return { hostLabel: parsed.hostLabel || host, repoSources: parsed.repoSources || [] };
245
+ }
246
+ /** Reads the keys a source's checkout currently holds on the host, so the merged result can be
247
+ worked out without needing that source's private key locally. */
248
+ async function readRemoteSourceKeys(config) {
249
+ let { host, repoURL } = config;
250
+ let repoPath = (0, sources_1.sourceRepoPath)(repoURL);
251
+ let output = await (0, remoteSSH_1.runOverSSH)({
252
+ host,
253
+ script: `${remoteSSH_1.SUDO_PREAMBLE}
254
+ if $SUDO test -f "${repoPath}/authorized_keys"; then
255
+ $SUDO cat "${repoPath}/authorized_keys"
256
+ elif $SUDO test -d "${repoPath}"; then
257
+ $SUDO cat "${repoPath}"/*.pub 2>/dev/null || true
258
+ fi`,
259
+ allowFailure: true,
260
+ });
261
+ return (0, authorizedKeys_1.normalizeKeys)(output.stdout);
262
+ }
263
+ async function installDaemon(config) {
264
+ let { host, hostLabel, repoSources } = config;
265
+ for (let command of ["node", "git", "yarn"]) {
266
+ if (!await (0, remoteSSH_1.remoteCommandExists)({ host, command })) {
267
+ throw new Error(`Expected ${command} to be installed on ${host}, it is not. Install it and rerun.`);
268
+ }
269
+ }
270
+ // The checkout is brought to the latest commit rather than a copy being pushed, so what runs on
271
+ // the host is exactly what is on github.
272
+ console.log(`Updating ${REMOTE_CHECKOUT_PATH} on ${host}`);
273
+ await (0, remoteSSH_1.runOverSSH)({
274
+ host,
275
+ script: `${remoteSSH_1.SUDO_PREAMBLE}
276
+ set -e
277
+ $SUDO mkdir -p "${path_1.default.posix.dirname(REMOTE_CHECKOUT_PATH)}"
278
+ if $SUDO test -d "${REMOTE_CHECKOUT_PATH}/.git"; then
279
+ # Stashed first, the way machine-alwaysup does it. Anything sitting modified in a host's
280
+ # checkout is either an accident or somebody editing the daemon in place, and either way it is
281
+ # worth keeping rather than quietly erasing. The identity is passed inline because a host has
282
+ # no git config of its own and stash writes a commit.
283
+ $SUDO git -C "${REMOTE_CHECKOUT_PATH}" add --all
284
+ $SUDO git -C "${REMOTE_CHECKOUT_PATH}" -c user.email=portsecure@localhost -c user.name=portsecure stash
285
+ $SUDO git -C "${REMOTE_CHECKOUT_PATH}" fetch --prune origin
286
+ # A stash and a pull cannot cross a branch that has diverged, and a drifted checkout must not be
287
+ # able to leave a host running old code, so it is put on the remote's state rather than merged
288
+ # with it. set-head so that is the remote's default branch now, not the one it had at clone.
289
+ $SUDO git -C "${REMOTE_CHECKOUT_PATH}" remote set-head origin --auto
290
+ $SUDO git -C "${REMOTE_CHECKOUT_PATH}" reset --hard origin/HEAD
291
+ else
292
+ $SUDO rm -rf "${REMOTE_CHECKOUT_PATH}"
293
+ $SUDO git clone "${SLIFTUTILS_URL}" "${REMOTE_CHECKOUT_PATH}"
294
+ fi
295
+ $SUDO yarn --cwd "${REMOTE_CHECKOUT_PATH}" install --production --non-interactive
296
+ # The single file daemon this replaced, left over on a host set up by an older version.
297
+ $SUDO rm -f /opt/portsecure/portsecure-daemon.js`,
298
+ });
299
+ await (0, remoteSSH_1.writeRemoteFile)({
300
+ host,
301
+ filePath: REMOTE_CONFIG_PATH,
302
+ // Only what differs between machines. Every path the daemon uses is derived in the daemon
303
+ // itself, so there is nothing here to drift out of sync.
304
+ contents: JSON.stringify({ hostLabel, repoSources }, undefined, 4) + "\n",
305
+ fileMode: "600",
306
+ directoryMode: "700",
307
+ });
308
+ await (0, remoteSSH_1.writeRemoteFile)({
309
+ host,
310
+ filePath: REMOTE_SERVICE_PATH,
311
+ contents: await promises_1.default.readFile(SERVICE_SOURCE, "utf8"),
312
+ fileMode: "644",
313
+ directoryMode: "755",
314
+ });
315
+ await (0, remoteSSH_1.runOverSSH)({
316
+ host,
317
+ script: `${remoteSSH_1.SUDO_PREAMBLE}
318
+ set -e
319
+ $SUDO systemctl daemon-reload
320
+ $SUDO systemctl enable ${SERVICE_NAME}
321
+ $SUDO systemctl restart ${SERVICE_NAME}`,
322
+ });
323
+ let status = (await (0, remoteSSH_1.runOverSSH)({
324
+ host,
325
+ script: `systemctl is-active ${SERVICE_NAME} || true`,
326
+ allowFailure: true,
327
+ })).stdout.trim();
328
+ if (status !== "active") {
329
+ let journal = (await (0, remoteSSH_1.runOverSSH)({
330
+ host,
331
+ script: `${remoteSSH_1.SUDO_PREAMBLE}
332
+ $SUDO journalctl -u ${SERVICE_NAME} -n 40 --no-pager || true`,
333
+ allowFailure: true,
334
+ })).stdout;
335
+ throw new Error(`Expected ${SERVICE_NAME} to be active on ${host}, was ${status}.\n${journal.slice(-2000)}`);
336
+ }
337
+ let stillReachable = await (0, remoteSSH_1.runOverSSH)({ host, script: "echo reachable", allowFailure: true });
338
+ if (stillReachable.stdout.trim() !== "reachable") {
339
+ throw new Error(`Expected ${host} to still be reachable after the daemon started, it is not.`
340
+ + ` Check console access immediately.`);
341
+ }
342
+ }
343
+ async function requireRemoteWebhook(host) {
344
+ let contents = await (0, remoteSSH_1.readRemoteFile)({ host, filePath: discord_1.DEFAULT_WEBHOOK_FILE_PATH });
345
+ if (!contents) {
346
+ throw new Error(`Expected a Discord webhook at ${discord_1.DEFAULT_WEBHOOK_FILE_PATH} on ${host}, no such file exists.`
347
+ + ` The daemon will not start without one.\n`
348
+ + `Set it up first:\n yarn setupnotify ${host} <discord-webhook-url>`);
349
+ }
350
+ return (0, discord_1.parseWebhookFile)({ contents, sourceName: `${host}:${discord_1.DEFAULT_WEBHOOK_FILE_PATH}` });
351
+ }
352
+ async function addSource(config) {
353
+ let { host, keyPath, repoURL } = config;
354
+ if (!await pathExists(keyPath)) {
355
+ throw new Error(`Expected a private key at ${keyPath}, no such file exists`);
356
+ }
357
+ console.log(`Checking ${repoURL} is reachable with ${keyPath}`);
358
+ let reachable = await gitWithKey({ keyPath, args: ["ls-remote", repoURL], allowFailure: true });
359
+ if (reachable.status !== 0) {
360
+ throw new Error(`Expected ${repoURL} to be reachable with ${keyPath}, git ls-remote failed.`
361
+ + ` The daemon would have no way to fetch keys.\n`
362
+ + `${(reachable.stdout + reachable.stderr).trim().slice(0, MAX_ERROR_BODY_LENGTH)}`);
363
+ }
364
+ let remoteConfig = await readRemoteConfig(host);
365
+ if (remoteConfig.repoSources.includes(repoURL)) {
366
+ console.log(`${host} already has ${repoURL}, refreshing its key and the daemon.`);
367
+ }
368
+ // The merged result is what root ends up with, so our own key has to be somewhere in it.
369
+ console.log(`Checking our access to ${host} survives the merged keys`);
370
+ let ourFingerprint = await findAuthenticatingFingerprint(host);
371
+ let inspectionPath = await cloneRepoForInspection({ repoURL, keyPath });
372
+ let newKeys = await (0, authorizedKeys_1.readRepoKeys)(inspectionPath);
373
+ // Whatever is applied on the host came from the existing sources, so it stays in the merge.
374
+ let existingKeys = (0, authorizedKeys_1.normalizeKeys)(await (0, remoteSSH_1.readRemoteFile)({ host, filePath: ROOT_AUTHORIZED_KEYS }) || "");
375
+ let mergedFingerprints = await fingerprintKeys([...existingKeys, ...newKeys]);
376
+ if (!mergedFingerprints.includes(ourFingerprint)) {
377
+ throw new Error(`Expected the key we use for ${host} to be in the merged keys, it is not.\n`
378
+ + `Ours: ${ourFingerprint}\n`
379
+ + `Merged: ${mergedFingerprints.join("\n ") || "(none)"}\n`
380
+ + `The daemon replaces root's authorized_keys with the merged sources, so this would`
381
+ + ` lock you out of ${host}. Add your public key to ${repoURL} first.`);
382
+ }
383
+ console.log(`Our key ${ourFingerprint} is in the merged keys, access will survive.`);
384
+ await ensureRevokeRepo({ keyPath, repoURL });
385
+ // Deploying a repo that still holds a revoked key would hand it back to every machine.
386
+ let revoked = await (0, unrevoke_1.revokedKeysInRepo)({ repoPath: inspectionPath, sourceURL: repoURL });
387
+ if (revoked.length) {
388
+ throw new Error(`Expected ${repoURL} to hold no revoked keys, it holds ${revoked.length}:\n`
389
+ + revoked.map(entry => {
390
+ var _a;
391
+ return ` ${entry.revocation.fingerprint} revoked by`
392
+ + ` ${entry.revocation.revokedBy || "?"} after use from ${((_a = entry.revocation.attempt) === null || _a === void 0 ? void 0 : _a.ip) || "?"}`;
393
+ }).join("\n")
394
+ + `\nDelete them from the repo, or run "yarn unrevoke" there to allow them again.`);
395
+ }
396
+ let webhookURL = await requireRemoteWebhook(host);
397
+ console.log(`${host} notifies ${webhookURL}`);
398
+ await (0, remoteSSH_1.writeRemoteFile)({
399
+ host,
400
+ filePath: (0, sources_1.sourceKeyPath)(repoURL),
401
+ contents: await promises_1.default.readFile(keyPath, "utf8"),
402
+ fileMode: "600",
403
+ directoryMode: "700",
404
+ });
405
+ let repoSources = remoteConfig.repoSources.filter(source => source !== repoURL);
406
+ repoSources.push(repoURL);
407
+ await installDaemon({ host, hostLabel: remoteConfig.hostLabel, repoSources });
408
+ console.log(`${repoURL} added to ${host}. ${repoSources.length} source(s) now merged.`);
409
+ }
410
+ async function removeSource(config) {
411
+ let { host, repoURL } = config;
412
+ let remoteConfig = await readRemoteConfig(host);
413
+ if (!remoteConfig.repoSources.includes(repoURL)) {
414
+ throw new Error(`Expected ${repoURL} to be a source on ${host}, it is not.\n`
415
+ + `Configured:\n ${remoteConfig.repoSources.join("\n ") || "(none)"}`);
416
+ }
417
+ let repoSources = remoteConfig.repoSources.filter(source => source !== repoURL);
418
+ if (repoSources.length) {
419
+ // The keys left over are what root gets, so our own key has to be among them.
420
+ console.log(`Checking our access to ${host} survives without ${repoURL}`);
421
+ let ourFingerprint = await findAuthenticatingFingerprint(host);
422
+ let remainingKeys = [];
423
+ for (let source of repoSources) {
424
+ remainingKeys.push(...await readRemoteSourceKeys({ host, repoURL: source }));
425
+ }
426
+ let remainingFingerprints = await fingerprintKeys(remainingKeys);
427
+ if (!remainingFingerprints.includes(ourFingerprint)) {
428
+ throw new Error(`Expected the key we use for ${host} to still be in the remaining sources, it is not.\n`
429
+ + `Ours: ${ourFingerprint}\n`
430
+ + `Remaining: ${remainingFingerprints.join("\n ") || "(none)"}\n`
431
+ + `Removing ${repoURL} would lock you out of ${host}.`);
432
+ }
433
+ }
434
+ else {
435
+ // Nothing left to merge, so the daemon leaves root's authorized_keys exactly as it is.
436
+ console.log(`${repoURL} is the last source, so root's authorized_keys stays as it is now.`);
437
+ }
438
+ await requireRemoteWebhook(host);
439
+ await (0, remoteSSH_1.runOverSSH)({
440
+ host,
441
+ script: `${remoteSSH_1.SUDO_PREAMBLE}
442
+ $SUDO rm -f "${(0, sources_1.sourceKeyPath)(repoURL)}"
443
+ $SUDO rm -rf "${(0, sources_1.sourceRepoPath)(repoURL)}"`,
444
+ });
445
+ await installDaemon({ host, hostLabel: remoteConfig.hostLabel, repoSources });
446
+ console.log(`${repoURL} removed from ${host}. ${repoSources.length} source(s) left.`);
447
+ }
448
+ /** Answers "who can log into this box, and which repo says so". The paths the daemon uses are
449
+ left out on purpose, they are plumbing rather than something to act on. */
450
+ /** Pushes the current daemon onto a host that already has one, for when this code has moved on.
451
+ Nothing about which keys the host trusts is touched. */
452
+ async function updateDaemon(host) {
453
+ let contents = await (0, remoteSSH_1.readRemoteFile)({ host, filePath: REMOTE_CONFIG_PATH });
454
+ if (!contents) {
455
+ throw new Error(`Expected ${host} to already have portsecure, ${REMOTE_CONFIG_PATH} does not exist.\n`
456
+ + `Set it up with:\n yarn securessh ${host} add <repo-private-key> [repo-url]`);
457
+ }
458
+ let parsed = JSON.parse(contents);
459
+ let repoSources = parsed.repoSources || [];
460
+ await requireRemoteWebhook(host);
461
+ await ensureRevokeReposExist(repoSources);
462
+ await installDaemon({ host, hostLabel: parsed.hostLabel || host, repoSources });
463
+ console.log(`Updated the daemon on ${host}, and restarted it.`);
464
+ console.log(`Its ${repoSources.length} key source(s) were left as they are, along with the keys and`);
465
+ console.log(`signers it has already accepted. Only the daemon itself changed:`);
466
+ for (let repoURL of repoSources) {
467
+ console.log(` ${repoURL}`);
468
+ }
469
+ }
470
+ async function listSources(host) {
471
+ let remoteConfig = await readRemoteConfig(host);
472
+ if (!remoteConfig.repoSources.length) {
473
+ console.log(`${host} has no key sources. root's authorized_keys is left exactly as it is.`);
474
+ return;
475
+ }
476
+ console.log(`${host} lets root log in with the keys from ${remoteConfig.repoSources.length} repo(s):`);
477
+ let merged = new Set();
478
+ for (let repoURL of remoteConfig.repoSources) {
479
+ let keys = await readRemoteSourceKeys({ host, repoURL });
480
+ console.log(`\n ${repoURL}`);
481
+ if (!keys.length) {
482
+ console.log(` grants no keys - the checkout is missing or empty`);
483
+ continue;
484
+ }
485
+ console.log(` grants ${keys.length} key(s):`);
486
+ for (let key of keys) {
487
+ console.log(` ${(0, authorizedKeys_1.summarizeKey)(key)}`);
488
+ merged.add(key);
489
+ }
490
+ }
491
+ if (remoteConfig.repoSources.length > 1) {
492
+ console.log(`\n${merged.size} key(s) in total once duplicates are merged.`);
493
+ }
494
+ }
495
+ /** The verb is a fixed word rather than a position, so it is pulled out of the arguments wherever
496
+ it was typed and everything left over is positional. */
497
+ function parseArgs(argv) {
498
+ let verbs = argv.filter(arg => VERBS.includes(arg));
499
+ if (!verbs.length) {
500
+ throw new Error(`Expected one of ${VERBS.join(", ")} somewhere in the arguments, was ${argv.join(" ") || "(nothing)"}\n${USAGE}`);
501
+ }
502
+ if (verbs.length > 1) {
503
+ throw new Error(`Expected one of ${VERBS.join(", ")}, was ${verbs.join(" and ")}\n${USAGE}`);
504
+ }
505
+ let verb = verbs[0];
506
+ let [host, ...rest] = argv.filter(arg => arg !== verb);
507
+ if (!host) {
508
+ throw new Error(`Expected a host, was nothing\n${USAGE}`);
509
+ }
510
+ return { verb, host, rest };
511
+ }
512
+ async function main() {
513
+ let { verb, host, rest } = parseArgs(process.argv.slice(2));
514
+ if (verb === "list") {
515
+ await listSources(host);
516
+ return;
517
+ }
518
+ if (verb === "update") {
519
+ if (rest.length) {
520
+ throw new Error(`Expected nothing after update, was ${rest.length} argument(s)\n${USAGE}`);
521
+ }
522
+ await updateDaemon(host);
523
+ return;
524
+ }
525
+ if (verb === "add") {
526
+ if (!rest.length || rest.length > 2) {
527
+ throw new Error(`Expected a private key and optionally a repo url, was ${rest.length} argument(s)\n${USAGE}`);
528
+ }
529
+ await addSource({ host, keyPath: (0, paths_1.expandHome)(rest[0]), repoURL: await resolveRepoURL(rest[1]) });
530
+ return;
531
+ }
532
+ if (rest.length > 1) {
533
+ throw new Error(`Expected at most a repo url to remove, was ${rest.length} argument(s)\n${USAGE}`);
534
+ }
535
+ await removeSource({ host, repoURL: await resolveRepoURL(rest[0]) });
536
+ }
537
+ exports.main = main;
538
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdXJlU1NILmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic2VjdXJlU1NILnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDJEQUE2QjtBQUM3Qiw0Q0FBb0I7QUFDcEIsZ0RBQXdCO0FBQ3hCLHNEQUF1RjtBQUN2RixxREFBNkU7QUFDN0UsdUNBQTBEO0FBQzFELGlEQUFrRjtBQUNsRix5Q0FBK0M7QUFDL0MsNENBQThDO0FBQzlDLDRDQUFnRDtBQUNoRCxvREFBdUg7QUFFdkgsTUFBTSxjQUFjLEdBQUcsY0FBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsUUFBUSxFQUFFLG9CQUFvQixDQUFDLENBQUM7QUFDNUUsa0dBQWtHO0FBQ2xHLG1HQUFtRztBQUNuRyxnQkFBZ0I7QUFDaEIsTUFBTSxjQUFjLEdBQUcsNENBQTRDLENBQUM7QUFDcEUsTUFBTSxvQkFBb0IsR0FBRyw0QkFBNEIsQ0FBQztBQUMxRCxNQUFNLG1CQUFtQixHQUFHLHdDQUF3QyxDQUFDO0FBQ3JFLE1BQU0sa0JBQWtCLEdBQUcsNkJBQTZCLENBQUM7QUFDekQsTUFBTSxvQkFBb0IsR0FBRyw0QkFBNEIsQ0FBQztBQUMxRCxNQUFNLFlBQVksR0FBRyxZQUFZLENBQUM7QUFDbEMsTUFBTSxxQkFBcUIsR0FBRyxHQUFHLENBQUM7QUFDbEMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQztBQUNsRCw4RUFBOEU7QUFDOUUsTUFBTSxLQUFLLEdBQUc7Ozs7K0JBSWlCLENBQUM7QUFFaEMsS0FBSyxVQUFVLFVBQVUsQ0FBQyxRQUFnQjtJQUN0QyxJQUFJLENBQUM7UUFDRCxNQUFNLGtCQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzFCLE9BQU8sSUFBSSxDQUFDO0lBQ2hCLENBQUM7SUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ1QsT0FBTyxLQUFLLENBQUM7SUFDakIs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Q0FBQztRQUNoRyxPQUFPO0lBQ1gsQ0FBQztJQUNELElBQUksSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNsQixNQUFNLElBQUksS0FBSyxDQUFDLDhDQUE4QyxJQUFJLENBQUMsTUFBTSxpQkFBaUIsS0FBSyxFQUFFLENBQUMsQ0FBQztJQUN2RyxDQUFDO0lBQ0QsTUFBTSxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUN6RSxDQUFDO0FBekJELG9CQXlCQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tIFwiZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBvcyBmcm9tIFwib3NcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBERUZBVUxUX1dFQkhPT0tfRklMRV9QQVRILCBwYXJzZVdlYmhvb2tGaWxlIH0gZnJvbSBcIi4uL25vdGlmaWNhdGlvbnMvZGlzY29yZFwiO1xuaW1wb3J0IHsgbm9ybWFsaXplS2V5cywgcmVhZFJlcG9LZXlzLCBzdW1tYXJpemVLZXkgfSBmcm9tIFwiLi9hdXRob3JpemVkS2V5c1wiO1xuaW1wb3J0IHsgc291cmNlS2V5UGF0aCwgc291cmNlUmVwb1BhdGggfSBmcm9tIFwiLi9zb3VyY2VzXCI7XG5pbXBvcnQgeyBkZXJpdmVSZXZva2VLZXksIFJFVk9LRV9LRVlfTEFCRUwsIHJldm9rZVJlcG9VUkwgfSBmcm9tIFwiLi9yZXZva2VTb3VyY2VcIjtcbmltcG9ydCB7IHJldm9rZWRLZXlzSW5SZXBvIH0gZnJvbSBcIi4vdW5yZXZva2VcIjtcbmltcG9ydCB7IGV4cGFuZEhvbWUgfSBmcm9tIFwiLi4vaGVscGVycy9wYXRoc1wiO1xuaW1wb3J0IHsgc3Bhd25Qcm9taXNlIH0gZnJvbSBcIi4uL2hlbHBlcnMvc3Bhd25cIjtcbmltcG9ydCB7IHJlYWRSZW1vdGVGaWxlLCByZW1vdGVDb21tYW5kRXhpc3RzLCBydW5PdmVyU1NILCBTVURPX1BSRUFNQkxFLCB3cml0ZVJlbW90ZUZpbGUgfSBmcm9tIFwiLi4vaGVscGVycy9yZW1vdGVTU0hcIjtcblxuY29uc3QgU0VSVklDRV9TT1VSQ0UgPSBwYXRoLmpvaW4oX19kaXJuYW1lLCBcImRhZW1vblwiLCBcInBvcnRzZWN1cmUuc2VydmljZVwiKTtcbi8vIFRoZSBkYWVtb24gcnVucyBmcm9tIGEgY2hlY2tvdXQgb2YgdGhpcyByZXBvIG9uIHRoZSBob3N0LCByYXRoZXIgdGhhbiBmcm9tIGEgY29weSB3ZSB1cGxvYWQsIHNvXG4vLyBhIGhvc3QgdXBkYXRlcyBpdHNlbGYgZnJvbSBnaXRodWIgdGhlIHNhbWUgd2F5IGFueXRoaW5nIGVsc2UgZG9lcy4gc2xpZnR1dGlscyBpcyBwdWJsaWMsIHNvIHRoaXNcbi8vIG5lZWRzIG5vIGtleS5cbmNvbnN0IFNMSUZUVVRJTFNfVVJMID0gXCJodHRwczovL2dpdGh1Yi5jb20vc2xpZnRpc3Qvc2xpZnR1dGlscy5naXRcIjtcbmNvbnN0IFJFTU9URV9DSEVDS09VVF9QQVRIID0gXCIvb3B0L3BvcnRzZWN1cmUvc2xpZnR1dGlsc1wiO1xuY29uc3QgUkVNT1RFX1NFUlZJQ0VfUEFUSCA9IFwiL2V0Yy9zeXN0ZW1kL3N5c3RlbS9wb3J0c2VjdXJlLnNlcnZpY2VcIjtcbmNvbnN0IFJFTU9URV9DT05GSUdfUEFUSCA9IFwiL2V0Yy9wb3J0c2VjdXJlL2RhZW1vbi5qc29uXCI7XG5jb25zdCBST09UX0FVVEhPUklaRURfS0VZUyA9IFwiL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXNcIjtcbmNvbnN0IFNFUlZJQ0VfTkFNRSA9IFwicG9ydHNlY3VyZVwiO1xuY29uc3QgTUFYX0VSUk9SX0JPRFlfTEVOR1RIID0gNTAwO1xuY29uc3QgVkVSQlMgPSBbXCJhZGRcIiwgXCJyZW1vdmVcIiwgXCJsaXN0XCIsIFwidXBkYXRlXCJdO1xuLy8gVGhlIHJlcG8gdXJsIGlzIG9wdGlvbmFsLCBhbmQgZGVmYXVsdHMgdG8gdGhlIHJlcG8gdGhlIGNvbW1hbmQgaXMgcnVuIGZyb20uXG5jb25zdCBVU0FHRSA9IGBVc2FnZTpcbiAgeWFybiBzZWN1cmVzc2ggPGhvc3Q+IGFkZCA8cmVwby1wcml2YXRlLWtleT4gW3JlcG8tdXJsXVxuICB5YXJuIHNlY3VyZXNzaCA8aG9zdD4gcmVtb3ZlIFtyZXBvLXVybF1cbiAgeWFybiBzZWN1cmVzc2ggPGhvc3Q+IGxpc3RcbiAgeWFybiBzZWN1cmVzc2ggPGhvc3Q+IHVwZGF0ZWA7XG5cbmFzeW5jIGZ1bmN0aW9uIHBhdGhFeGlzdHMoZmlsZVBhdGg6IHN0cmluZykge1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZzLmFjY2VzcyhmaWxlUGF0aCk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gcnVuTG9jYWwoY29uZmlnOiB7IGNvbW1hbmQ6IHN0cmluZzsgYXJnczogc3RyaW5nW107IGN3ZD86IHN0cmluZzsgYWxsb3dGYWlsdXJlPzogYm9vbGVhbiB9KSB7XG4gICAgbGV0IHsgY29tbWFuZCwgYXJncywgY3dkLCBhbGxvd0ZhaWx1cmUgfSA9IGNvbmZpZztcbiAgICBsZXQgcmVzdWx0ID0gYXdhaXQgc3Bhd25Qcm9taXNlKHsgY29tbWFuZCwgYXJncywgY3dkIH0pO1xuICAgIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCAke2NvbW1hbmR9IHRvIHJ1biwgZmFpbGVkIHdpdGggJHtyZXN1bHQuZXJyb3IubWVzc2FnZX1gKTtcbiAgICB9XG4gICAgaWYgKHJlc3VsdC5zdGF0dXMgIT09IDAgJiYgIWFsbG93RmFpbHVyZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgJHtjb21tYW5kfSAke2FyZ3Muam9pbihcIiBcIil9IHRvIGV4aXQgMCwgd2FzICR7cmVzdWx0LnN0YXR1c30uIGBcbiAgICAgICAgICAgICsgYCR7KHJlc3VsdC5zdGRvdXQgKyByZXN1bHQuc3RkZXJyKS50cmltKCkuc2xpY2UoMCwgTUFYX0VSUk9SX0JPRFlfTEVOR1RIKX1gXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKiBBIHByaXZhdGUga2V5IGNhbm5vdCBhdXRoZW50aWNhdGUgYW4gaHR0cHMgcmVtb3RlLCBzbyBnaXRodWIgdXJscyBhcmUgY29udmVydGVkIHRvIHRoZSBzc2ggZm9ybVxuICAgIHRoZSBrZXkgY2FuIGFjdHVhbGx5IGJlIHVzZWQgd2l0aC4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZVJlcG9VUkwocmVwb1VSTDogc3RyaW5nKSB7XG4gICAgbGV0IGh0dHBzTWF0Y2ggPSByZXBvVVJMLm1hdGNoKC9eaHR0cHM/OlxcL1xcLyhbXi9dKylcXC8oLis/KSg/OlxcLmdpdCk/XFwvPyQvKTtcbiAgICBpZiAoIWh0dHBzTWF0Y2gpIHtcbiAgICAgICAgcmV0dXJuIHJlcG9VUkw7XG4gICAgfVxuICAgIGxldCBbLCBob3N0LCByZXBvUGF0aF0gPSBodHRwc01hdGNoO1xuICAgIHJldHVybiBgZ2l0QCR7aG9zdH06JHtyZXBvUGF0aH0uZ2l0YDtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZ2l0V2l0aEtleShjb25maWc6IHsga2V5UGF0aDogc3RyaW5nOyBhcmdzOiBzdHJpbmdbXTsgY3dkPzogc3RyaW5nOyBhbGxvd0ZhaWx1cmU/OiBib29sZWFuIH0pIHtcbiAgICBsZXQgeyBrZXlQYXRoLCBhcmdzLCBjd2QsIGFsbG93RmFpbHVyZSB9ID0gY29uZmlnO1xuICAgIC8vIGNvcmUuc3NoQ29tbWFuZCBrZWVwcyBrZXkgc2VsZWN0aW9uIHdpdGggdGhlIGNvbW1hbmQgaW5zdGVhZCBvZiBpbiB0aGUgZW52aXJvbm1lbnQuXG4gICAgbGV0IHNzaENvbW1hbmQgPSBgc3NoIC1pICR7a2V5UGF0aH0gLW8gSWRlbnRpdGllc09ubHk9eWVzIC1vIEJhdGNoTW9kZT15ZXMgLW8gU3RyaWN0SG9zdEtleUNoZWNraW5nPWFjY2VwdC1uZXdgO1xuICAgIGxldCByZXN1bHQgPSBhd2FpdCBzcGF3blByb21pc2UoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzOiBbXCItY1wiLCBgY29yZS5zc2hDb21tYW5kPSR7c3NoQ29tbWFuZH1gLCAuLi5hcmdzXSwgY3dkIH0pO1xuICAgIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBnaXQgdG8gcnVuLCBmYWlsZWQgd2l0aCAke3Jlc3VsdC5lcnJvci5tZXNzYWdlfWApO1xuICAgIH1cbiAgICBpZiAocmVzdWx0LnN0YXR1cyAhPT0gMCAmJiAhYWxsb3dGYWlsdXJlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBFeHBlY3RlZCBnaXQgJHthcmdzLmpvaW4oXCIgXCIpfSB0byBleGl0IDAsIHdhcyAke3Jlc3VsdC5zdGF0dXN9LiBgXG4gICAgICAgICAgICArIGAkeyhyZXN1bHQuc3Rkb3V0ICsgcmVzdWx0LnN0ZGVycikudHJpbSgpLnNsaWNlKDAsIE1BWF9FUlJPUl9CT0RZX0xFTkdUSCl9YFxuICAgICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKiogQXNrcyBzc2ggd2hpY2gga2V5IGl0IGFjdHVhbGx5IGF1dGhlbnRpY2F0ZWQgd2l0aC4gVGhpcyBpcyB0aGUga2V5IHRoYXQgbXVzdCBzdXJ2aXZlIHRoZVxuICAgIGRhZW1vbiB0YWtpbmcgb3ZlciBhdXRob3JpemVkX2tleXMsIG90aGVyd2lzZSBhIGRlcGxveSBsb2NrcyB1cyBvdXQuICovXG5hc3luYyBmdW5jdGlvbiBmaW5kQXV0aGVudGljYXRpbmdGaW5nZXJwcmludChob3N0OiBzdHJpbmcpIHtcbiAgICBsZXQgcmVzdWx0ID0gYXdhaXQgc3Bhd25Qcm9taXNlKHtcbiAgICAgICAgY29tbWFuZDogXCJzc2hcIixcbiAgICAgICAgYXJnczogW1wiLXZcIiwgXCItb1wiLCBcIkJhdGNoTW9kZT15ZXNcIiwgXCItb1wiLCBcIkNvbm5lY3RUaW1lb3V0PTEwXCIsIGhvc3QsIFwidHJ1ZVwiXSxcbiAgICB9KTtcbiAgICBsZXQgdmVyYm9zZU91dHB1dCA9IHJlc3VsdC5zdGRlcnIgfHwgXCJcIjtcbiAgICBpZiAocmVzdWx0LnN0YXR1cyAhPT0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIHRvIHNzaCBpbnRvICR7aG9zdH0sIGZhaWxlZC4gJHt2ZXJib3NlT3V0cHV0LnNsaWNlKC1NQVhfRVJST1JfQk9EWV9MRU5HVEgpfWApO1xuICAgIH1cbiAgICBsZXQgYWNjZXB0ZWRNYXRjaCA9IHZlcmJvc2VPdXRwdXQubWF0Y2goL1NlcnZlciBhY2NlcHRzIGtleTouKj8oU0hBMjU2OltBLVphLXowLTkrLz1dKykvKTtcbiAgICBpZiAoIWFjY2VwdGVkTWF0Y2gpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkICR7aG9zdH0gdG8gYWNjZXB0IGEgcHVibGljIGtleSwgYnV0IHRoZSBzZXNzaW9uIGRpZCBub3QgYXV0aGVudGljYXRlIHdpdGggb25lLmBcbiAgICAgICAgICAgICsgYCBwb3J0c2VjdXJlIGRpc2FibGVzIHBhc3N3b3JkIGxvZ2luLCBzbyBrZXkgYmFzZWQgYWNjZXNzIGhhcyB0byB3b3JrIGZpcnN0LmBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIGFjY2VwdGVkTWF0Y2hbMV07XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZpbmdlcnByaW50S2V5cyhrZXlzOiBzdHJpbmdbXSkge1xuICAgIGlmICgha2V5cy5sZW5ndGgpIHtcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICBsZXQgdGVtcG9yYXJ5RGlyZWN0b3J5ID0gYXdhaXQgZnMubWtkdGVtcChwYXRoLmpvaW4ob3MudG1wZGlyKCksIFwicG9ydHNlY3VyZS1rZXlzLVwiKSk7XG4gICAgbGV0IGtleXNQYXRoID0gcGF0aC5qb2luKHRlbXBvcmFyeURpcmVjdG9yeSwgXCJhdXRob3JpemVkX2tleXNcIik7XG4gICAgYXdhaXQgZnMud3JpdGVGaWxlKGtleXNQYXRoLCBgJHtrZXlzLmpvaW4oXCJcXG5cIil9XFxuYCk7XG4gICAgbGV0IHJlc3VsdCA9IGF3YWl0IHJ1bkxvY2FsKHsgY29tbWFuZDogXCJzc2gta2V5Z2VuXCIsIGFyZ3M6IFtcIi1sZlwiLCBrZXlzUGF0aF0sIGFsbG93RmFpbHVyZTogdHJ1ZSB9KTtcbiAgICBhd2FpdCBmcy5ybSh0ZW1wb3JhcnlEaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICBsZXQgZmluZ2VycHJpbnRzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGZvciAobGV0IGxpbmUgb2YgcmVzdWx0LnN0ZG91dC5zcGxpdChcIlxcblwiKSkge1xuICAgICAgICBsZXQgbWF0Y2ggPSBsaW5lLm1hdGNoKC8oU0hBMjU2OltBLVphLXowLTkrLz1dKykvKTtcbiAgICAgICAgaWYgKG1hdGNoKSB7XG4gICAgICAgICAgICBmaW5nZXJwcmludHMucHVzaChtYXRjaFsxXSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZpbmdlcnByaW50cztcbn1cblxuYXN5bmMgZnVuY3Rpb24gY2xvbmVSZXBvRm9ySW5zcGVjdGlvbihjb25maWc6IHsgcmVwb1VSTDogc3RyaW5nOyBrZXlQYXRoOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IHJlcG9VUkwsIGtleVBhdGggfSA9IGNvbmZpZztcbiAgICBsZXQgdGVtcG9yYXJ5RGlyZWN0b3J5ID0gYXdhaXQgZnMubWtkdGVtcChwYXRoLmpvaW4ob3MudG1wZGlyKCksIFwicG9ydHNlY3VyZS1yZXBvLVwiKSk7XG4gICAgbGV0IHJlcG9QYXRoID0gcGF0aC5qb2luKHRlbXBvcmFyeURpcmVjdG9yeSwgXCJyZXBvXCIpO1xuICAgIGF3YWl0IGdpdFdpdGhLZXkoeyBrZXlQYXRoLCBhcmdzOiBbXCJjbG9uZVwiLCBcIi0tZGVwdGhcIiwgXCIxXCIsIHJlcG9VUkwsIHJlcG9QYXRoXSB9KTtcbiAgICByZXR1cm4gcmVwb1BhdGg7XG59XG5cbi8qKiBXaXRoIG5vIHJlcG8gdXJsIGdpdmVuLCB0aGUgcmVwbyB3ZSBhcmUgc3RhbmRpbmcgaW4gaXMgdXNlZC4gSXQgaGFzIHRvIGFjdHVhbGx5IGhvbGQga2V5c1xuICAgIGJlZm9yZSB3ZSBoYW5kIGl0IHRvIGEgaG9zdCwgc28gYSBzdHJheSB3b3JraW5nIGRpcmVjdG9yeSBjYW5ub3QgYmUgZGVwbG95ZWQgYnkgYWNjaWRlbnQuICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlUmVwb1VSTChwYXNzZWRVUkw6IHN0cmluZyB8IHVuZGVmaW5lZCkge1xuICAgIGlmIChwYXNzZWRVUkwpIHtcbiAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZVJlcG9VUkwocGFzc2VkVVJMKTtcbiAgICB9XG4gICAgbGV0IHRvcExldmVsID0gYXdhaXQgcnVuTG9jYWwoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzOiBbXCJyZXYtcGFyc2VcIiwgXCItLXNob3ctdG9wbGV2ZWxcIl0sIGFsbG93RmFpbHVyZTogdHJ1ZSB9KTtcbiAgICBpZiAodG9wTGV2ZWwuc3RhdHVzICE9PSAwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSByZXBvIHVybCwgb3IgdGhlIGN1cnJlbnQgZGlyZWN0b3J5IHRvIGJlIGluc2lkZSBhIGdpdCByZXBvLCBpdCBpcyBub3QuXFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgbGV0IHJlcG9QYXRoID0gdG9wTGV2ZWwuc3Rkb3V0LnRyaW0oKTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCByZWFkUmVwb0tleXMocmVwb1BhdGgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkIGEgcmVwbyB1cmwsIG9yIHRoZSBjdXJyZW50IHJlcG8gKCR7cmVwb1BhdGh9KSB0byBob2xkIGtleXMsIGl0IGRvZXMgbm90LlxcbiR7ZX1cXG4ke1VTQUdFfWBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgbGV0IG9yaWdpbiA9IGF3YWl0IHJ1bkxvY2FsKHtcbiAgICAgICAgY29tbWFuZDogXCJnaXRcIixcbiAgICAgICAgYXJnczogW1wicmVtb3RlXCIsIFwiZ2V0LXVybFwiLCBcIm9yaWdpblwiXSxcbiAgICAgICAgY3dkOiByZXBvUGF0aCxcbiAgICAgICAgYWxsb3dGYWlsdXJlOiB0cnVlLFxuICAgIH0pO1xuICAgIGlmIChvcmlnaW4uc3RhdHVzICE9PSAwIHx8ICFvcmlnaW4uc3Rkb3V0LnRyaW0oKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgdGhlIGN1cnJlbnQgcmVwbyAoJHtyZXBvUGF0aH0pIHRvIGhhdmUgYW4gb3JpZ2luIHJlbW90ZSwgaXQgaGFzIG5vbmUuYFxuICAgICAgICAgICAgKyBgIFRoZSBob3N0IGNsb25lcyB0aGUgc291cmNlIGl0c2VsZiwgc28gYSBsb2NhbCBwYXRoIGlzIG5vIHVzZSB0byBpdC5gXG4gICAgICAgICk7XG4gICAgfVxuICAgIGxldCByZXBvVVJMID0gbm9ybWFsaXplUmVwb1VSTChvcmlnaW4uc3Rkb3V0LnRyaW0oKSk7XG4gICAgY29uc29sZS5sb2coYE5vIHJlcG8gdXJsIGdpdmVuLCB1c2luZyB0aGUgY3VycmVudCByZXBvOiAke3JlcG9VUkx9YCk7XG4gICAgcmV0dXJuIHJlcG9VUkw7XG59XG5cbi8qKiBFdmVyeSBzb3VyY2UncyByZXZva2UgcmVwbyBoYXMgdG8gZXhpc3QgYW5kIGhvbGQgYXQgbGVhc3Qgb25lIGNvbW1pdCwgb3IgdGhlIGhvc3RzIHVzaW5nIGl0XG4gICAgY2Fubm90IHJlY29yZCBhIHJldm9jYXRpb24uIENoZWNrZWQgd2l0aCB0aGlzIG1hY2hpbmUncyBvd24gZ2l0IGNyZWRlbnRpYWxzLCBzaW5jZSB1cGRhdGUgaXNcbiAgICBub3QgZ2l2ZW4gYW55IGRlcGxveSBrZXksIGFuZCBhbiBlbXB0eSByZXBvIGlzIGluaXRpYWxpc2VkIHJhdGhlciB0aGFuIG1lcmVseSBjb21wbGFpbmVkIGFib3V0LlxuXG4gICAgQSBob3N0IHRoYXQgY2Fubm90IHdyaXRlIGEgcmV2b2NhdGlvbiBzaWxlbnRseSBrZWVwcyBhY2NlcHRpbmcgYSBrZXkgaXQganVzdCBzYXcgYmVpbmcgbWlzdXNlZCxcbiAgICB3aGljaCBpcyB0aGUgb25lIGZhaWx1cmUgdGhpcyB3aG9sZSB0aGluZyBleGlzdHMgdG8gcHJldmVudCwgc28gaXQgaXMgY2hlY2tlZCBvbiBldmVyeSBkZXBsb3lcbiAgICBhbmQgbm90IG9ubHkgd2hlbiBhIHNvdXJjZSBpcyBmaXJzdCBhZGRlZC4gKi9cbmFzeW5jIGZ1bmN0aW9uIGVuc3VyZVJldm9rZVJlcG9zRXhpc3QocmVwb1NvdXJjZXM6IHN0cmluZ1tdKSB7XG4gICAgZm9yIChsZXQgcmVwb1VSTCBvZiByZXBvU291cmNlcykge1xuICAgICAgICBsZXQgcmV2b2tlVVJMID0gcmV2b2tlUmVwb1VSTChyZXBvVVJMKTtcbiAgICAgICAgbGV0IHRlbXBvcmFyeURpcmVjdG9yeSA9IGF3YWl0IGZzLm1rZHRlbXAocGF0aC5qb2luKG9zLnRtcGRpcigpLCBcInBvcnRzZWN1cmUtcmV2b2tlLVwiKSk7XG4gICAgICAgIGxldCBjaGVja291dFBhdGggPSBwYXRoLmpvaW4odGVtcG9yYXJ5RGlyZWN0b3J5LCBcInJlcG9cIik7XG4gICAgICAgIGxldCBjbG9uZSA9IGF3YWl0IHJ1bkxvY2FsKHsgY29tbWFuZDogXCJnaXRcIiwgYXJnczogW1wiY2xvbmVcIiwgcmV2b2tlVVJMLCBjaGVja291dFBhdGhdLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgICAgIGlmIChjbG9uZS5zdGF0dXMgIT09IDApIHtcbiAgICAgICAgICAgIGF3YWl0IGZzLnJtKHRlbXBvcmFyeURpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgIGBFeHBlY3RlZCAke3Jldm9rZVVSTH0gdG8gZXhpc3QsIGl0IGRvZXMgbm90LCBzbyAke3JlcG9VUkx9IGhhcyBub3doZXJlIHRvIHJlY29yZCBhYFxuICAgICAgICAgICAgICAgICsgYCByZXZvY2F0aW9uLlxcbkNyZWF0ZSBpdCwgdGhlbiBydW4gXCJ5YXJuIHNlY3VyZXNzaCA8aG9zdD4gYWRkXCIgZm9yIHRoYXQgc291cmNlIHRvYFxuICAgICAgICAgICAgICAgICsgYCByZWdpc3RlciBpdHMgZGVwbG95IGtleS5cXG4keyhjbG9uZS5zdGRvdXQgKyBjbG9uZS5zdGRlcnIpLnRyaW0oKS5zbGljZSgwLCBNQVhfRVJST1JfQk9EWV9MRU5HVEgpfWBcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IGhlYWQgPSBhd2FpdCBydW5Mb2NhbCh7IGNvbW1hbmQ6IFwiZ2l0XCIsIGFyZ3M6IFtcIi1DXCIsIGNoZWNrb3V0UGF0aCwgXCJyZXYtcGFyc2VcIiwgXCJIRUFEXCJdLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgICAgIGlmIChoZWFkLnN0YXR1cyAhPT0gMCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYCR7cmV2b2tlVVJMfSBpcyBlbXB0eSwgZ2l2aW5nIGl0IGEgZmlyc3QgY29tbWl0YCk7XG4gICAgICAgICAgICBhd2FpdCBmcy53cml0ZUZpbGUocGF0aC5qb2luKGNoZWNrb3V0UGF0aCwgXCJSRUFETUUubWRcIiksXG4gICAgICAgICAgICAgICAgYCMgcmV2b2tlZCBrZXlzXFxuXFxuV3JpdHRlbiBieSBwb3J0c2VjdXJlLiBFYWNoIGZpbGUgdW5kZXIgcmV2b2NhdGlvbnMvIGlzIG9uZSBrZXkgdGhhdCB3YXMgdXNlZCBmcm9tIGFuXFxuYFxuICAgICAgICAgICAgICAgICsgYGFkZHJlc3MgaXQgaXMgbm90IGFsbG93ZWQgZnJvbSwgYW5kIGlzIG5vIGxvbmdlciBhY2NlcHRlZCBhbnl3aGVyZS5cXG5gKTtcbiAgICAgICAgICAgIGZvciAobGV0IGFyZ3Mgb2YgW1xuICAgICAgICAgICAgICAgIFtcIi1DXCIsIGNoZWNrb3V0UGF0aCwgXCJhZGRcIiwgXCItQVwiXSxcbiAgICAgICAgICAgICAgICBbXCItQ1wiLCBjaGVja291dFBhdGgsIFwiLWNcIiwgXCJ1c2VyLmVtYWlsPXBvcnRzZWN1cmVAbG9jYWxob3N0XCIsIFwiLWNcIiwgXCJ1c2VyLm5hbWU9cG9ydHNlY3VyZVwiLCBcImNvbW1pdFwiLCBcIi1tXCIsIFwiaW5pdGlhbGlzZSByZXZva2UgcmVwb1wiXSxcbiAgICAgICAgICAgICAgICBbXCItQ1wiLCBjaGVja291dFBhdGgsIFwicHVzaFwiLCBcIm9yaWdpblwiLCBcIkhFQURcIl0sXG4gICAgICAgICAgICBdKSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgcnVuTG9jYWwoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGF3YWl0IGZzLnJtKHRlbXBvcmFyeURpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICBjb25zb2xlLmxvZyhgJHtyZXZva2VVUkx9IGlzIHJlYWR5YCk7XG4gICAgfVxufVxuXG4vKiogVGhlIHJldm9rZSByZXBvIGhhcyB0byBleGlzdCBhbmQgYmUgd3JpdGFibGUgYmVmb3JlIGEgaG9zdCBpcyBzZXQgdXAsIGJlY2F1c2UgYSBob3N0IHRoYXRcbiAgICBjYW5ub3Qgd3JpdGUgYSByZXZvY2F0aW9uIGNhbm5vdCByZXZva2UgYSBrZXkgdGhhdCBpcyBiZWluZyBtaXN1c2VkLiBUaGUga2V5IGZvciBpdCBpcyBkZXJpdmVkXG4gICAgZnJvbSB0aGUgc291cmNlJ3MsIHNpbmNlIGdpdGh1YiB3aWxsIG5vdCB0YWtlIG9uZSBwdWJsaWMga2V5IG9uIHR3byByZXBvcy4gKi9cbmFzeW5jIGZ1bmN0aW9uIGVuc3VyZVJldm9rZVJlcG8oY29uZmlnOiB7IGtleVBhdGg6IHN0cmluZzsgcmVwb1VSTDogc3RyaW5nIH0pIHtcbiAgICBsZXQgeyBrZXlQYXRoLCByZXBvVVJMIH0gPSBjb25maWc7XG4gICAgbGV0IHJldm9rZVVSTCA9IHJldm9rZVJlcG9VUkwocmVwb1VSTCk7XG4gICAgbGV0IGRlcml2ZWQgPSBkZXJpdmVSZXZva2VLZXkoYXdhaXQgZnMucmVhZEZpbGUoa2V5UGF0aCwgXCJ1dGY4XCIpKTtcblxuICAgIGxldCB0ZW1wb3JhcnlEaXJlY3RvcnkgPSBhd2FpdCBmcy5ta2R0ZW1wKHBhdGguam9pbihvcy50bXBkaXIoKSwgXCJwb3J0c2VjdXJlLXJldm9rZS1cIikpO1xuICAgIGxldCBkZXJpdmVkS2V5UGF0aCA9IHBhdGguam9pbih0ZW1wb3JhcnlEaXJlY3RvcnksIFwia2V5XCIpO1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZShkZXJpdmVkS2V5UGF0aCwgZGVyaXZlZC5wcml2YXRlS2V5RmlsZSwgeyBtb2RlOiAwbzYwMCB9KTtcbiAgICBsZXQgY2hlY2tvdXRQYXRoID0gcGF0aC5qb2luKHRlbXBvcmFyeURpcmVjdG9yeSwgXCJyZXBvXCIpO1xuXG4gICAgbGV0IGV4cGxhaW4gPSAocHJvYmxlbTogc3RyaW5nKSA9PiBuZXcgRXJyb3IoXG4gICAgICAgIGAke3Byb2JsZW19XFxuYFxuICAgICAgICArIGBDcmVhdGUgJHtyZXZva2VVUkx9IGFuZCBhZGQgdGhpcyBhcyBhIGRlcGxveSBrZXkgV0lUSCBXUklURSBBQ0NFU1M6XFxuYFxuICAgICAgICArIGAgICR7ZGVyaXZlZC5wdWJsaWNLZXl9ICR7UkVWT0tFX0tFWV9MQUJFTH1cXG5gXG4gICAgICAgICsgYEl0IGhhcyB0byBiZSB0aGlzIGtleTogaXQgaXMgZGVyaXZlZCBmcm9tICR7a2V5UGF0aH0sIGFuZCBnaXRodWIgd2lsbCBub3QgYWNjZXB0IHRoZSBzYW1lYFxuICAgICAgICArIGAgcHVibGljIGtleSBvbiB0d28gcmVwb3NpdG9yaWVzLmBcbiAgICApO1xuXG4gICAgY29uc29sZS5sb2coYENoZWNraW5nICR7cmV2b2tlVVJMfWApO1xuICAgIGxldCBjbG9uZSA9IGF3YWl0IGdpdFdpdGhLZXkoeyBrZXlQYXRoOiBkZXJpdmVkS2V5UGF0aCwgYXJnczogW1wiY2xvbmVcIiwgcmV2b2tlVVJMLCBjaGVja291dFBhdGhdLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgaWYgKGNsb25lLnN0YXR1cyAhPT0gMCkge1xuICAgICAgICB0aHJvdyBleHBsYWluKGBFeHBlY3RlZCAke3Jldm9rZVVSTH0gdG8gYmUgcmVhZGFibGUgd2l0aCB0aGUgZGVyaXZlZCBrZXksIGl0IGlzIG5vdC5gKTtcbiAgICB9XG5cbiAgICAvLyBBbiBlbXB0eSByZXBvIGhhcyBubyBicmFuY2ggZm9yIHRoZSBkYWVtb24gdG8gY2xvbmUsIHNvIGl0IGdldHMgaXRzIGZpcnN0IGNvbW1pdCBoZXJlLiBUaGF0XG4gICAgLy8gZG91YmxlcyBhcyB0aGUgcHJvb2YgdGhhdCB3ZSBjYW4gd3JpdGUgdG8gaXQuXG4gICAgbGV0IGhlYWQgPSBhd2FpdCBnaXRXaXRoS2V5KHsga2V5UGF0aDogZGVyaXZlZEtleVBhdGgsIGFyZ3M6IFtcInJldi1wYXJzZVwiLCBcIkhFQURcIl0sIGN3ZDogY2hlY2tvdXRQYXRoLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgaWYgKGhlYWQuc3RhdHVzICE9PSAwKSB7XG4gICAgICAgIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oY2hlY2tvdXRQYXRoLCBcIlJFQURNRS5tZFwiKSxcbiAgICAgICAgICAgIGAjIHJldm9rZWQga2V5c1xcblxcbldyaXR0ZW4gYnkgcG9ydHNlY3VyZS4gRWFjaCBmaWxlIHVuZGVyIHJldm9jYXRpb25zLyBpcyBvbmUga2V5IHRoYXQgd2FzIHVzZWQgZnJvbSBhblxcbmBcbiAgICAgICAgICAgICsgYGFkZHJlc3MgaXQgaXMgbm90IGFsbG93ZWQgZnJvbSwgYW5kIGlzIG5vIGxvbmdlciBhY2NlcHRlZCBhbnl3aGVyZS5cXG5gKTtcbiAgICAgICAgZm9yIChsZXQgYXJncyBvZiBbXG4gICAgICAgICAgICBbXCJhZGRcIiwgXCItQVwiXSxcbiAgICAgICAgICAgIFtcIi1jXCIsIFwidXNlci5lbWFpbD1wb3J0c2VjdXJlQGxvY2FsaG9zdFwiLCBcIi1jXCIsIFwidXNlci5uYW1lPXBvcnRzZWN1cmVcIiwgXCJjb21taXRcIiwgXCItbVwiLCBcImluaXRpYWxpc2UgcmV2b2tlIHJlcG9cIl0sXG4gICAgICAgIF0pIHtcbiAgICAgICAgICAgIGF3YWl0IGdpdFdpdGhLZXkoeyBrZXlQYXRoOiBkZXJpdmVkS2V5UGF0aCwgYXJncywgY3dkOiBjaGVja291dFBhdGggfSk7XG4gICAgICAgIH1cbiAgICAgICAgbGV0IHB1c2ggPSBhd2FpdCBnaXRXaXRoS2V5KHsga2V5UGF0aDogZGVyaXZlZEtleVBhdGgsIGFyZ3M6IFtcInB1c2hcIiwgXCJvcmlnaW5cIiwgXCJIRUFEXCJdLCBjd2Q6IGNoZWNrb3V0UGF0aCwgYWxsb3dGYWlsdXJlOiB0cnVlIH0pO1xuICAgICAgICBpZiAocHVzaC5zdGF0dXMgIT09IDApIHtcbiAgICAgICAgICAgIHRocm93IGV4cGxhaW4oYEV4cGVjdGVkIHdyaXRlIGFjY2VzcyB0byAke3Jldm9rZVVSTH0sIHRoZSBmaXJzdCBwdXNoIHdhcyByZWZ1c2VkLlxcbiR7KHB1c2guc3Rkb3V0ICsgcHVzaC5zdGRlcnIpLnRyaW0oKS5zbGljZSgwLCBNQVhfRVJST1JfQk9EWV9MRU5HVEgpfWApO1xuICAgICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbGV0IGRyeVJ1biA9IGF3YWl0IGdpdFdpdGhLZXkoeyBrZXlQYXRoOiBkZXJpdmVkS2V5UGF0aCwgYXJnczogW1wicHVzaFwiLCBcIi0tZHJ5LXJ1blwiLCBcIm9yaWdpblwiLCBcIkhFQURcIl0sIGN3ZDogY2hlY2tvdXRQYXRoLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgICAgIGlmIChkcnlSdW4uc3RhdHVzICE9PSAwKSB7XG4gICAgICAgICAgICB0aHJvdyBleHBsYWluKGBFeHBlY3RlZCB3cml0ZSBhY2Nlc3MgdG8gJHtyZXZva2VVUkx9LCBhIGRyeSBydW4gcHVzaCB3YXMgcmVmdXNlZC5cXG4keyhkcnlSdW4uc3Rkb3V0ICsgZHJ5UnVuLnN0ZGVycikudHJpbSgpLnNsaWNlKDAsIE1BWF9FUlJPUl9CT0RZX0xFTkdUSCl9YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCBmcy5ybSh0ZW1wb3JhcnlEaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgICBjb25zb2xlLmxvZyhgJHtyZXZva2VVUkx9IGlzIHdyaXRhYmxlYCk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRSZW1vdGVDb25maWcoaG9zdDogc3RyaW5nKSB7XG4gICAgbGV0IGNvbnRlbnRzID0gYXdhaXQgcmVhZFJlbW90ZUZpbGUoeyBob3N0LCBmaWxlUGF0aDogUkVNT1RFX0NPTkZJR19QQVRIIH0pO1xuICAgIGlmICghY29udGVudHMpIHtcbiAgICAgICAgcmV0dXJuIHsgaG9zdExhYmVsOiBob3N0LCByZXBvU291cmNlczogW10gYXMgc3RyaW5nW10gfTtcbiAgICB9XG4gICAgbGV0IHBhcnNlZCA9IEpTT04ucGFyc2UoY29udGVudHMpIGFzIHsgaG9zdExhYmVsPzogc3RyaW5nOyByZXBvU291cmNlcz86IHN0cmluZ1tdIH07XG4gICAgcmV0dXJuIHsgaG9zdExhYmVsOiBwYXJzZWQuaG9zdExhYmVsIHx8IGhvc3QsIHJlcG9Tb3VyY2VzOiBwYXJzZWQucmVwb1NvdXJjZXMgfHwgW10gfTtcbn1cblxuLyoqIFJlYWRzIHRoZSBrZXlzIGEgc291cmNlJ3MgY2hlY2tvdXQgY3VycmVudGx5IGhvbGRzIG9uIHRoZSBob3N0LCBzbyB0aGUgbWVyZ2VkIHJlc3VsdCBjYW4gYmVcbiAgICB3b3JrZWQgb3V0IHdpdGhvdXQgbmVlZGluZyB0aGF0IHNvdXJjZSdzIHByaXZhdGUga2V5IGxvY2FsbHkuICovXG5hc3luYyBmdW5jdGlvbiByZWFkUmVtb3RlU291cmNlS2V5cyhjb25maWc6IHsgaG9zdDogc3RyaW5nOyByZXBvVVJMOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IGhvc3QsIHJlcG9VUkwgfSA9IGNvbmZpZztcbiAgICBsZXQgcmVwb1BhdGggPSBzb3VyY2VSZXBvUGF0aChyZXBvVVJMKTtcbiAgICBsZXQgb3V0cHV0ID0gYXdhaXQgcnVuT3ZlclNTSCh7XG4gICAgICAgIGhvc3QsXG4gICAgICAgIHNjcmlwdDogYCR7U1VET19QUkVBTUJMRX1cbmlmICRTVURPIHRlc3QgLWYgXCIke3JlcG9QYXRofS9hdXRob3JpemVkX2tleXNcIjsgdGhlblxuICAgICRTVURPIGNhdCBcIiR7cmVwb1BhdGh9L2F1dGhvcml6ZWRfa2V5c1wiXG5lbGlmICRTVURPIHRlc3QgLWQgXCIke3JlcG9QYXRofVwiOyB0aGVuXG4gICAgJFNVRE8gY2F0IFwiJHtyZXBvUGF0aH1cIi8qLnB1YiAyPi9kZXYvbnVsbCB8fCB0cnVlXG5maWAsXG4gICAgICAgIGFsbG93RmFpbHVyZTogdHJ1ZSxcbiAgICB9KTtcbiAgICByZXR1cm4gbm9ybWFsaXplS2V5cyhvdXRwdXQuc3Rkb3V0KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaW5zdGFsbERhZW1vbihjb25maWc6IHsgaG9zdDogc3RyaW5nOyBob3N0TGFiZWw6IHN0cmluZzsgcmVwb1NvdXJjZXM6IHN0cmluZ1tdIH0pIHtcbiAgICBsZXQgeyBob3N0LCBob3N0TGFiZWwsIHJlcG9Tb3VyY2VzIH0gPSBjb25maWc7XG4gICAgZm9yIChsZXQgY29tbWFuZCBvZiBbXCJub2RlXCIsIFwiZ2l0XCIsIFwieWFyblwiXSkge1xuICAgICAgICBpZiAoIWF3YWl0IHJlbW90ZUNvbW1hbmRFeGlzdHMoeyBob3N0LCBjb21tYW5kIH0pKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7Y29tbWFuZH0gdG8gYmUgaW5zdGFsbGVkIG9uICR7aG9zdH0sIGl0IGlzIG5vdC4gSW5zdGFsbCBpdCBhbmQgcmVydW4uYCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUaGUgY2hlY2tvdXQgaXMgYnJvdWdodCB0byB0aGUgbGF0ZXN0IGNvbW1pdCByYXRoZXIgdGhhbiBhIGNvcHkgYmVpbmcgcHVzaGVkLCBzbyB3aGF0IHJ1bnMgb25cbiAgICAvLyB0aGUgaG9zdCBpcyBleGFjdGx5IHdoYXQgaXMgb24gZ2l0aHViLlxuICAgIGNvbnNvbGUubG9nKGBVcGRhdGluZyAke1JFTU9URV9DSEVDS09VVF9QQVRIfSBvbiAke2hvc3R9YCk7XG4gICAgYXdhaXQgcnVuT3ZlclNTSCh7XG4gICAgICAgIGhvc3QsXG4gICAgICAgIHNjcmlwdDogYCR7U1VET19QUkVBTUJMRX1cbnNldCAtZVxuJFNVRE8gbWtkaXIgLXAgXCIke3BhdGgucG9zaXguZGlybmFtZShSRU1PVEVfQ0hFQ0tPVVRfUEFUSCl9XCJcbmlmICRTVURPIHRlc3QgLWQgXCIke1JFTU9URV9DSEVDS09VVF9QQVRIfS8uZ2l0XCI7IHRoZW5cbiAgICAjIFN0YXNoZWQgZmlyc3QsIHRoZSB3YXkgbWFjaGluZS1hbHdheXN1cCBkb2VzIGl0LiBBbnl0aGluZyBzaXR0aW5nIG1vZGlmaWVkIGluIGEgaG9zdCdzXG4gICAgIyBjaGVja291dCBpcyBlaXRoZXIgYW4gYWNjaWRlbnQgb3Igc29tZWJvZHkgZWRpdGluZyB0aGUgZGFlbW9uIGluIHBsYWNlLCBhbmQgZWl0aGVyIHdheSBpdCBpc1xuICAgICMgd29ydGgga2VlcGluZyByYXRoZXIgdGhhbiBxdWlldGx5IGVyYXNpbmcuIFRoZSBpZGVudGl0eSBpcyBwYXNzZWQgaW5saW5lIGJlY2F1c2UgYSBob3N0IGhhc1xuICAgICMgbm8gZ2l0IGNvbmZpZyBvZiBpdHMgb3duIGFuZCBzdGFzaCB3cml0ZXMgYSBjb21taXQuXG4gICAgJFNVRE8gZ2l0IC1DIFwiJHtSRU1PVEVfQ0hFQ0tPVVRfUEFUSH1cIiBhZGQgLS1hbGxcbiAgICAkU1VETyBnaXQgLUMgXCIke1JFTU9URV9DSEVDS09VVF9QQVRIfVwiIC1jIHVzZXIuZW1haWw9cG9ydHNlY3VyZUBsb2NhbGhvc3QgLWMgdXNlci5uYW1lPXBvcnRzZWN1cmUgc3Rhc2hcbiAgICAkU1VETyBnaXQgLUMgXCIke1JFTU9URV9DSEVDS09VVF9QQVRIfVwiIGZldGNoIC0tcHJ1bmUgb3JpZ2luXG4gICAgIyBBIHN0YXNoIGFuZCBhIHB1bGwgY2Fubm90IGNyb3NzIGEgYnJhbmNoIHRoYXQgaGFzIGRpdmVyZ2VkLCBhbmQgYSBkcmlmdGVkIGNoZWNrb3V0IG11c3Qgbm90IGJlXG4gICAgIyBhYmxlIHRvIGxlYXZlIGEgaG9zdCBydW5uaW5nIG9sZCBjb2RlLCBzbyBpdCBpcyBwdXQgb24gdGhlIHJlbW90ZSdzIHN0YXRlIHJhdGhlciB0aGFuIG1lcmdlZFxuICAgICMgd2l0aCBpdC4gc2V0LWhlYWQgc28gdGhhdCBpcyB0aGUgcmVtb3RlJ3MgZGVmYXVsdCBicmFuY2ggbm93LCBub3QgdGhlIG9uZSBpdCBoYWQgYXQgY2xvbmUuXG4gICAgJFNVRE8gZ2l0IC1DIFwiJHtSRU1PVEVfQ0hFQ0tPVVRfUEFUSH1cIiByZW1vdGUgc2V0LWhlYWQgb3JpZ2luIC0tYXV0b1xuICAgICRTVURPIGdpdCAtQyBcIiR7UkVNT1RFX0NIRUNLT1VUX1BBVEh9XCIgcmVzZXQgLS1oYXJkIG9yaWdpbi9IRUFEXG5lbHNlXG4gICAgJFNVRE8gcm0gLXJmIFwiJHtSRU1PVEVfQ0hFQ0tPVVRfUEFUSH1cIlxuICAgICRTVURPIGdpdCBjbG9uZSBcIiR7U0xJRlRVVElMU19VUkx9XCIgXCIke1JFTU9URV9DSEVDS09VVF9QQVRIfVwiXG5maVxuJFNVRE8geWFybiAtLWN3ZCBcIiR7UkVNT1RFX0NIRUNLT1VUX1BBVEh9XCIgaW5zdGFsbCAtLXByb2R1Y3Rpb24gLS1ub24taW50ZXJhY3RpdmVcbiMgVGhlIHNpbmdsZSBmaWxlIGRhZW1vbiB0aGlzIHJlcGxhY2VkLCBsZWZ0IG92ZXIgb24gYSBob3N0IHNldCB1cCBieSBhbiBvbGRlciB2ZXJzaW9uLlxuJFNVRE8gcm0gLWYgL29wdC9wb3J0c2VjdXJlL3BvcnRzZWN1cmUtZGFlbW9uLmpzYCxcbiAgICB9KTtcbiAgICBhd2FpdCB3cml0ZVJlbW90ZUZpbGUoe1xuICAgICAgICBob3N0LFxuICAgICAgICBmaWxlUGF0aDogUkVNT1RFX0NPTkZJR19QQVRILFxuICAgICAgICAvLyBPbmx5IHdoYXQgZGlmZmVycyBiZXR3ZWVuIG1hY2hpbmVzLiBFdmVyeSBwYXRoIHRoZSBkYWVtb24gdXNlcyBpcyBkZXJpdmVkIGluIHRoZSBkYWVtb25cbiAgICAgICAgLy8gaXRzZWxmLCBzbyB0aGVyZSBpcyBub3RoaW5nIGhlcmUgdG8gZHJpZnQgb3V0IG9mIHN5bmMuXG4gICAgICAgIGNvbnRlbnRzOiBKU09OLnN0cmluZ2lmeSh7IGhvc3RMYWJlbCwgcmVwb1NvdXJjZXMgfSwgdW5kZWZpbmVkLCA0KSArIFwiXFxuXCIsXG4gICAgICAgIGZpbGVNb2RlOiBcIjYwMFwiLFxuICAgICAgICBkaXJlY3RvcnlNb2RlOiBcIjcwMFwiLFxuICAgIH0pO1xuICAgIGF3YWl0IHdyaXRlUmVtb3RlRmlsZSh7XG4gICAgICAgIGhvc3QsXG4gICAgICAgIGZpbGVQYXRoOiBSRU1PVEVfU0VSVklDRV9QQVRILFxuICAgICAgICBjb250ZW50czogYXdhaXQgZnMucmVhZEZpbGUoU0VSVklDRV9TT1VSQ0UsIFwidXRmOFwiKSxcbiAgICAgICAgZmlsZU1vZGU6IFwiNjQ0XCIsXG4gICAgICAgIGRpcmVjdG9yeU1vZGU6IFwiNzU1XCIsXG4gICAgfSk7XG4gICAgYXdhaXQgcnVuT3ZlclNTSCh7XG4gICAgICAgIGhvc3QsXG4gICAgICAgIHNjcmlwdDogYCR7U1VET19QUkVBTUJMRX1cbnNldCAtZVxuJFNVRE8gc3lzdGVtY3RsIGRhZW1vbi1yZWxvYWRcbiRTVURPIHN5c3RlbWN0bCBlbmFibGUgJHtTRVJWSUNFX05BTUV9XG4kU1VETyBzeXN0ZW1jdGwgcmVzdGFydCAke1NFUlZJQ0VfTkFNRX1gLFxuICAgIH0pO1xuXG4gICAgbGV0IHN0YXR1cyA9IChhd2FpdCBydW5PdmVyU1NIKHtcbiAgICAgICAgaG9zdCxcbiAgICAgICAgc2NyaXB0OiBgc3lzdGVtY3RsIGlzLWFjdGl2ZSAke1NFUlZJQ0VfTkFNRX0gfHwgdHJ1ZWAsXG4gICAgICAgIGFsbG93RmFpbHVyZTogdHJ1ZSxcbiAgICB9KSkuc3Rkb3V0LnRyaW0oKTtcbiAgICBpZiAoc3RhdHVzICE9PSBcImFjdGl2ZVwiKSB7XG4gICAgICAgIGxldCBqb3VybmFsID0gKGF3YWl0IHJ1bk92ZXJTU0goe1xuICAgICAgICAgICAgaG9zdCxcbiAgICAgICAgICAgIHNjcmlwdDogYCR7U1VET19QUkVBTUJMRX1cbiRTVURPIGpvdXJuYWxjdGwgLXUgJHtTRVJWSUNFX05BTUV9IC1uIDQwIC0tbm8tcGFnZXIgfHwgdHJ1ZWAsXG4gICAgICAgICAgICBhbGxvd0ZhaWx1cmU6IHRydWUsXG4gICAgICAgIH0pKS5zdGRvdXQ7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtTRVJWSUNFX05BTUV9IHRvIGJlIGFjdGl2ZSBvbiAke2hvc3R9LCB3YXMgJHtzdGF0dXN9LlxcbiR7am91cm5hbC5zbGljZSgtMjAwMCl9YCk7XG4gICAgfVxuXG4gICAgbGV0IHN0aWxsUmVhY2hhYmxlID0gYXdhaXQgcnVuT3ZlclNTSCh7IGhvc3QsIHNjcmlwdDogXCJlY2hvIHJlYWNoYWJsZVwiLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgaWYgKHN0aWxsUmVhY2hhYmxlLnN0ZG91dC50cmltKCkgIT09IFwicmVhY2hhYmxlXCIpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkICR7aG9zdH0gdG8gc3RpbGwgYmUgcmVhY2hhYmxlIGFmdGVyIHRoZSBkYWVtb24gc3RhcnRlZCwgaXQgaXMgbm90LmBcbiAgICAgICAgICAgICsgYCBDaGVjayBjb25zb2xlIGFjY2VzcyBpbW1lZGlhdGVseS5gXG4gICAgICAgICk7XG4gICAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiByZXF1aXJlUmVtb3RlV2ViaG9vayhob3N0OiBzdHJpbmcpIHtcbiAgICBsZXQgY29udGVudHMgPSBhd2FpdCByZWFkUmVtb3RlRmlsZSh7IGhvc3QsIGZpbGVQYXRoOiBERUZBVUxUX1dFQkhPT0tfRklMRV9QQVRIIH0pO1xuICAgIGlmICghY29udGVudHMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkIGEgRGlzY29yZCB3ZWJob29rIGF0ICR7REVGQVVMVF9XRUJIT09LX0ZJTEVfUEFUSH0gb24gJHtob3N0fSwgbm8gc3VjaCBmaWxlIGV4aXN0cy5gXG4gICAgICAgICAgICArIGAgVGhlIGRhZW1vbiB3aWxsIG5vdCBzdGFydCB3aXRob3V0IG9uZS5cXG5gXG4gICAgICAgICAgICArIGBTZXQgaXQgdXAgZmlyc3Q6XFxuICB5YXJuIHNldHVwbm90aWZ5ICR7aG9zdH0gPGRpc2NvcmQtd2ViaG9vay11cmw+YFxuICAgICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gcGFyc2VXZWJob29rRmlsZSh7IGNvbnRlbnRzLCBzb3VyY2VOYW1lOiBgJHtob3N0fToke0RFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEh9YCB9KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gYWRkU291cmNlKGNvbmZpZzogeyBob3N0OiBzdHJpbmc7IGtleVBhdGg6IHN0cmluZzsgcmVwb1VSTDogc3RyaW5nIH0pIHtcbiAgICBsZXQgeyBob3N0LCBrZXlQYXRoLCByZXBvVVJMIH0gPSBjb25maWc7XG4gICAgaWYgKCFhd2FpdCBwYXRoRXhpc3RzKGtleVBhdGgpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSBwcml2YXRlIGtleSBhdCAke2tleVBhdGh9LCBubyBzdWNoIGZpbGUgZXhpc3RzYCk7XG4gICAgfVxuXG4gICAgY29uc29sZS5sb2coYENoZWNraW5nICR7cmVwb1VSTH0gaXMgcmVhY2hhYmxlIHdpdGggJHtrZXlQYXRofWApO1xuICAgIGxldCByZWFjaGFibGUgPSBhd2FpdCBnaXRXaXRoS2V5KHsga2V5UGF0aCwgYXJnczogW1wibHMtcmVtb3RlXCIsIHJlcG9VUkxdLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgaWYgKHJlYWNoYWJsZS5zdGF0dXMgIT09IDApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkICR7cmVwb1VSTH0gdG8gYmUgcmVhY2hhYmxlIHdpdGggJHtrZXlQYXRofSwgZ2l0IGxzLXJlbW90ZSBmYWlsZWQuYFxuICAgICAgICAgICAgKyBgIFRoZSBkYWVtb24gd291bGQgaGF2ZSBubyB3YXkgdG8gZmV0Y2gga2V5cy5cXG5gXG4gICAgICAgICAgICArIGAkeyhyZWFjaGFibGUuc3Rkb3V0ICsgcmVhY2hhYmxlLnN0ZGVycikudHJpbSgpLnNsaWNlKDAsIE1BWF9FUlJPUl9CT0RZX0xFTkdUSCl9YFxuICAgICAgICApO1xuICAgIH1cblxuICAgIGxldCByZW1vdGVDb25maWcgPSBhd2FpdCByZWFkUmVtb3RlQ29uZmlnKGhvc3QpO1xuICAgIGlmIChyZW1vdGVDb25maWcucmVwb1NvdXJjZXMuaW5jbHVkZXMocmVwb1VSTCkpIHtcbiAgICAgICAgY29uc29sZS5sb2coYCR7aG9zdH0gYWxyZWFkeSBoYXMgJHtyZXBvVVJMfSwgcmVmcmVzaGluZyBpdHMga2V5IGFuZCB0aGUgZGFlbW9uLmApO1xuICAgIH1cblxuICAgIC8vIFRoZSBtZXJnZWQgcmVzdWx0IGlzIHdoYXQgcm9vdCBlbmRzIHVwIHdpdGgsIHNvIG91ciBvd24ga2V5IGhhcyB0byBiZSBzb21ld2hlcmUgaW4gaXQuXG4gICAgY29uc29sZS5sb2coYENoZWNraW5nIG91ciBhY2Nlc3MgdG8gJHtob3N0fSBzdXJ2aXZlcyB0aGUgbWVyZ2VkIGtleXNgKTtcbiAgICBsZXQgb3VyRmluZ2VycHJpbnQgPSBhd2FpdCBmaW5kQXV0aGVudGljYXRpbmdGaW5nZXJwcmludChob3N0KTtcbiAgICBsZXQgaW5zcGVjdGlvblBhdGggPSBhd2FpdCBjbG9uZVJlcG9Gb3JJbnNwZWN0aW9uKHsgcmVwb1VSTCwga2V5UGF0aCB9KTtcbiAgICBsZXQgbmV3S2V5cyA9IGF3YWl0IHJlYWRSZXBvS2V5cyhpbnNwZWN0aW9uUGF0aCk7XG4gICAgLy8gV2hhdGV2ZXIgaXMgYXBwbGllZCBvbiB0aGUgaG9zdCBjYW1lIGZyb20gdGhlIGV4aXN0aW5nIHNvdXJjZXMsIHNvIGl0IHN0YXlzIGluIHRoZSBtZXJnZS5cbiAgICBsZXQgZXhpc3RpbmdLZXlzID0gbm9ybWFsaXplS2V5cyhhd2FpdCByZWFkUmVtb3RlRmlsZSh7IGhvc3QsIGZpbGVQYXRoOiBST09UX0FVVEhPUklaRURfS0VZUyB9KSB8fCBcIlwiKTtcbiAgICBsZXQgbWVyZ2VkRmluZ2VycHJpbnRzID0gYXdhaXQgZmluZ2VycHJpbnRLZXlzKFsuLi5leGlzdGluZ0tleXMsIC4uLm5ld0tleXNdKTtcbiAgICBpZiAoIW1lcmdlZEZpbmdlcnByaW50cy5pbmNsdWRlcyhvdXJGaW5nZXJwcmludCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEV4cGVjdGVkIHRoZSBrZXkgd2UgdXNlIGZvciAke2hvc3R9IHRvIGJlIGluIHRoZSBtZXJnZWQga2V5cywgaXQgaXMgbm90LlxcbmBcbiAgICAgICAgICAgICsgYE91cnM6ICAgJHtvdXJGaW5nZXJwcmludH1cXG5gXG4gICAgICAgICAgICArIGBNZXJnZWQ6ICR7bWVyZ2VkRmluZ2VycHJpbnRzLmpvaW4oXCJcXG4gICAgICAgIFwiKSB8fCBcIihub25lKVwifVxcbmBcbiAgICAgICAgICAgICsgYFRoZSBkYWVtb24gcmVwbGFjZXMgcm9vdCdzIGF1dGhvcml6ZWRfa2V5cyB3aXRoIHRoZSBtZXJnZWQgc291cmNlcywgc28gdGhpcyB3b3VsZGBcbiAgICAgICAgICAgICsgYCBsb2NrIHlvdSBvdXQgb2YgJHtob3N0fS4gQWRkIHlvdXIgcHVibGljIGtleSB0byAke3JlcG9VUkx9IGZpcnN0LmBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgY29uc29sZS5sb2coYE91ciBrZXkgJHtvdXJGaW5nZXJwcmludH0gaXMgaW4gdGhlIG1lcmdlZCBrZXlzLCBhY2Nlc3Mgd2lsbCBzdXJ2aXZlLmApO1xuXG4gICAgYXdhaXQgZW5zdXJlUmV2b2tlUmVwbyh7IGtleVBhdGgsIHJlcG9VUkwgfSk7XG5cbiAgICAvLyBEZXBsb3lpbmcgYSByZXBvIHRoYXQgc3RpbGwgaG9sZHMgYSByZXZva2VkIGtleSB3b3VsZCBoYW5kIGl0IGJhY2sgdG8gZXZlcnkgbWFjaGluZS5cbiAgICBsZXQgcmV2b2tlZCA9IGF3YWl0IHJldm9rZWRLZXlzSW5SZXBvKHsgcmVwb1BhdGg6IGluc3BlY3Rpb25QYXRoLCBzb3VyY2VVUkw6IHJlcG9VUkwgfSk7XG4gICAgaWYgKHJldm9rZWQubGVuZ3RoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBFeHBlY3RlZCAke3JlcG9VUkx9IHRvIGhvbGQgbm8gcmV2b2tlZCBrZXlzLCBpdCBob2xkcyAke3Jldm9rZWQubGVuZ3RofTpcXG5gXG4gICAgICAgICAgICArIHJldm9rZWQubWFwKGVudHJ5ID0+IGAgICR7ZW50cnkucmV2b2NhdGlvbi5maW5nZXJwcmludH0gcmV2b2tlZCBieWBcbiAgICAgICAgICAgICAgICArIGAgJHtlbnRyeS5yZXZvY2F0aW9uLnJldm9rZWRCeSB8fCBcIj9cIn0gYWZ0ZXIgdXNlIGZyb20gJHtlbnRyeS5yZXZvY2F0aW9uLmF0dGVtcHQ/LmlwIHx8IFwiP1wifWApLmpvaW4oXCJcXG5cIilcbiAgICAgICAgICAgICsgYFxcbkRlbGV0ZSB0aGVtIGZyb20gdGhlIHJlcG8sIG9yIHJ1biBcInlhcm4gdW5yZXZva2VcIiB0aGVyZSB0byBhbGxvdyB0aGVtIGFnYWluLmBcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBsZXQgd2ViaG9va1VSTCA9IGF3YWl0IHJlcXVpcmVSZW1vdGVXZWJob29rKGhvc3QpO1xuICAgIGNvbnNvbGUubG9nKGAke2hvc3R9IG5vdGlmaWVzICR7d2ViaG9va1VSTH1gKTtcblxuICAgIGF3YWl0IHdyaXRlUmVtb3RlRmlsZSh7XG4gICAgICAgIGhvc3QsXG4gICAgICAgIGZpbGVQYXRoOiBzb3VyY2VLZXlQYXRoKHJlcG9VUkwpLFxuICAgICAgICBjb250ZW50czogYXdhaXQgZnMucmVhZEZpbGUoa2V5UGF0aCwgXCJ1dGY4XCIpLFxuICAgICAgICBmaWxlTW9kZTogXCI2MDBcIixcbiAgICAgICAgZGlyZWN0b3J5TW9kZTogXCI3MDBcIixcbiAgICB9KTtcblxuICAgIGxldCByZXBvU291cmNlcyA9IHJlbW90ZUNvbmZpZy5yZXBvU291cmNlcy5maWx0ZXIoc291cmNlID0+IHNvdXJjZSAhPT0gcmVwb1VSTCk7XG4gICAgcmVwb1NvdXJjZXMucHVzaChyZXBvVVJMKTtcbiAgICBhd2FpdCBpbnN0YWxsRGFlbW9uKHsgaG9zdCwgaG9zdExhYmVsOiByZW1vdGVDb25maWcuaG9zdExhYmVsLCByZXBvU291cmNlcyB9KTtcbiAgICBjb25zb2xlLmxvZyhgJHtyZXBvVVJMfSBhZGRlZCB0byAke2hvc3R9LiAke3JlcG9Tb3VyY2VzLmxlbmd0aH0gc291cmNlKHMpIG5vdyBtZXJnZWQuYCk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlbW92ZVNvdXJjZShjb25maWc6IHsgaG9zdDogc3RyaW5nOyByZXBvVVJMOiBzdHJpbmcgfSkge1xuICAgIGxldCB7IGhvc3QsIHJlcG9VUkwgfSA9IGNvbmZpZztcbiAgICBsZXQgcmVtb3RlQ29uZmlnID0gYXdhaXQgcmVhZFJlbW90ZUNvbmZpZyhob3N0KTtcbiAgICBpZiAoIXJlbW90ZUNvbmZpZy5yZXBvU291cmNlcy5pbmNsdWRlcyhyZXBvVVJMKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgJHtyZXBvVVJMfSB0byBiZSBhIHNvdXJjZSBvbiAke2hvc3R9LCBpdCBpcyBub3QuXFxuYFxuICAgICAgICAgICAgKyBgQ29uZmlndXJlZDpcXG4gICR7cmVtb3RlQ29uZmlnLnJlcG9Tb3VyY2VzLmpvaW4oXCJcXG4gIFwiKSB8fCBcIihub25lKVwifWBcbiAgICAgICAgKTtcbiAgICB9XG4gICAgbGV0IHJlcG9Tb3VyY2VzID0gcmVtb3RlQ29uZmlnLnJlcG9Tb3VyY2VzLmZpbHRlcihzb3VyY2UgPT4gc291cmNlICE9PSByZXBvVVJMKTtcblxuICAgIGlmIChyZXBvU291cmNlcy5sZW5ndGgpIHtcbiAgICAgICAgLy8gVGhlIGtleXMgbGVmdCBvdmVyIGFyZSB3aGF0IHJvb3QgZ2V0cywgc28gb3VyIG93biBrZXkgaGFzIHRvIGJlIGFtb25nIHRoZW0uXG4gICAgICAgIGNvbnNvbGUubG9nKGBDaGVja2luZyBvdXIgYWNjZXNzIHRvICR7aG9zdH0gc3Vydml2ZXMgd2l0aG91dCAke3JlcG9VUkx9YCk7XG4gICAgICAgIGxldCBvdXJGaW5nZXJwcmludCA9IGF3YWl0IGZpbmRBdXRoZW50aWNhdGluZ0ZpbmdlcnByaW50KGhvc3QpO1xuICAgICAgICBsZXQgcmVtYWluaW5nS2V5czogc3RyaW5nW10gPSBbXTtcbiAgICAgICAgZm9yIChsZXQgc291cmNlIG9mIHJlcG9Tb3VyY2VzKSB7XG4gICAgICAgICAgICByZW1haW5pbmdLZXlzLnB1c2goLi4uYXdhaXQgcmVhZFJlbW90ZVNvdXJjZUtleXMoeyBob3N0LCByZXBvVVJMOiBzb3VyY2UgfSkpO1xuICAgICAgICB9XG4gICAgICAgIGxldCByZW1haW5pbmdGaW5nZXJwcmludHMgPSBhd2FpdCBmaW5nZXJwcmludEtleXMocmVtYWluaW5nS2V5cyk7XG4gICAgICAgIGlmICghcmVtYWluaW5nRmluZ2VycHJpbnRzLmluY2x1ZGVzKG91ckZpbmdlcnByaW50KSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgIGBFeHBlY3RlZCB0aGUga2V5IHdlIHVzZSBmb3IgJHtob3N0fSB0byBzdGlsbCBiZSBpbiB0aGUgcmVtYWluaW5nIHNvdXJjZXMsIGl0IGlzIG5vdC5cXG5gXG4gICAgICAgICAgICAgICAgKyBgT3VyczogICAgICAke291ckZpbmdlcnByaW50fVxcbmBcbiAgICAgICAgICAgICAgICArIGBSZW1haW5pbmc6ICR7cmVtYWluaW5nRmluZ2VycHJpbnRzLmpvaW4oXCJcXG4gICAgICAgICAgIFwiKSB8fCBcIihub25lKVwifVxcbmBcbiAgICAgICAgICAgICAgICArIGBSZW1vdmluZyAke3JlcG9VUkx9IHdvdWxkIGxvY2sgeW91IG91dCBvZiAke2hvc3R9LmBcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgICAvLyBOb3RoaW5nIGxlZnQgdG8gbWVyZ2UsIHNvIHRoZSBkYWVtb24gbGVhdmVzIHJvb3QncyBhdXRob3JpemVkX2tleXMgZXhhY3RseSBhcyBpdCBpcy5cbiAgICAgICAgY29uc29sZS5sb2coYCR7cmVwb1VSTH0gaXMgdGhlIGxhc3Qgc291cmNlLCBzbyByb290J3MgYXV0aG9yaXplZF9rZXlzIHN0YXlzIGFzIGl0IGlzIG5vdy5gKTtcbiAgICB9XG5cbiAgICBhd2FpdCByZXF1aXJlUmVtb3RlV2ViaG9vayhob3N0KTtcbiAgICBhd2FpdCBydW5PdmVyU1NIKHtcbiAgICAgICAgaG9zdCxcbiAgICAgICAgc2NyaXB0OiBgJHtTVURPX1BSRUFNQkxFfVxuJFNVRE8gcm0gLWYgXCIke3NvdXJjZUtleVBhdGgocmVwb1VSTCl9XCJcbiRTVURPIHJtIC1yZiBcIiR7c291cmNlUmVwb1BhdGgocmVwb1VSTCl9XCJgLFxuICAgIH0pO1xuICAgIGF3YWl0IGluc3RhbGxEYWVtb24oeyBob3N0LCBob3N0TGFiZWw6IHJlbW90ZUNvbmZpZy5ob3N0TGFiZWwsIHJlcG9Tb3VyY2VzIH0pO1xuICAgIGNvbnNvbGUubG9nKGAke3JlcG9VUkx9IHJlbW92ZWQgZnJvbSAke2hvc3R9LiAke3JlcG9Tb3VyY2VzLmxlbmd0aH0gc291cmNlKHMpIGxlZnQuYCk7XG59XG5cbi8qKiBBbnN3ZXJzIFwid2hvIGNhbiBsb2cgaW50byB0aGlzIGJveCwgYW5kIHdoaWNoIHJlcG8gc2F5cyBzb1wiLiBUaGUgcGF0aHMgdGhlIGRhZW1vbiB1c2VzIGFyZVxuICAgIGxlZnQgb3V0IG9uIHB1cnBvc2UsIHRoZXkgYXJlIHBsdW1iaW5nIHJhdGhlciB0aGFuIHNvbWV0aGluZyB0byBhY3Qgb24uICovXG5cbi8qKiBQdXNoZXMgdGhlIGN1cnJlbnQgZGFlbW9uIG9udG8gYSBob3N0IHRoYXQgYWxyZWFkeSBoYXMgb25lLCBmb3Igd2hlbiB0aGlzIGNvZGUgaGFzIG1vdmVkIG9uLlxuICAgIE5vdGhpbmcgYWJvdXQgd2hpY2gga2V5cyB0aGUgaG9zdCB0cnVzdHMgaXMgdG91Y2hlZC4gKi9cbmFzeW5jIGZ1bmN0aW9uIHVwZGF0ZURhZW1vbihob3N0OiBzdHJpbmcpIHtcbiAgICBsZXQgY29udGVudHMgPSBhd2FpdCByZWFkUmVtb3RlRmlsZSh7IGhvc3QsIGZpbGVQYXRoOiBSRU1PVEVfQ09ORklHX1BBVEggfSk7XG4gICAgaWYgKCFjb250ZW50cykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgJHtob3N0fSB0byBhbHJlYWR5IGhhdmUgcG9ydHNlY3VyZSwgJHtSRU1PVEVfQ09ORklHX1BBVEh9IGRvZXMgbm90IGV4aXN0LlxcbmBcbiAgICAgICAgICAgICsgYFNldCBpdCB1cCB3aXRoOlxcbiAgeWFybiBzZWN1cmVzc2ggJHtob3N0fSBhZGQgPHJlcG8tcHJpdmF0ZS1rZXk+IFtyZXBvLXVybF1gXG4gICAgICAgICk7XG4gICAgfVxuICAgIGxldCBwYXJzZWQgPSBKU09OLnBhcnNlKGNvbnRlbnRzKSBhcyB7IGhvc3RMYWJlbD86IHN0cmluZzsgcmVwb1NvdXJjZXM/OiBzdHJpbmdbXSB9O1xuICAgIGxldCByZXBvU291cmNlcyA9IHBhcnNlZC5yZXBvU291cmNlcyB8fCBbXTtcbiAgICBhd2FpdCByZXF1aXJlUmVtb3RlV2ViaG9vayhob3N0KTtcbiAgICBhd2FpdCBlbnN1cmVSZXZva2VSZXBvc0V4aXN0KHJlcG9Tb3VyY2VzKTtcbiAgICBhd2FpdCBpbnN0YWxsRGFlbW9uKHsgaG9zdCwgaG9zdExhYmVsOiBwYXJzZWQuaG9zdExhYmVsIHx8IGhvc3QsIHJlcG9Tb3VyY2VzIH0pO1xuICAgIGNvbnNvbGUubG9nKGBVcGRhdGVkIHRoZSBkYWVtb24gb24gJHtob3N0fSwgYW5kIHJlc3RhcnRlZCBpdC5gKTtcbiAgICBjb25zb2xlLmxvZyhgSXRzICR7cmVwb1NvdXJjZXMubGVuZ3RofSBrZXkgc291cmNlKHMpIHdlcmUgbGVmdCBhcyB0aGV5IGFyZSwgYWxvbmcgd2l0aCB0aGUga2V5cyBhbmRgKTtcbiAgICBjb25zb2xlLmxvZyhgc2lnbmVycyBpdCBoYXMgYWxyZWFkeSBhY2NlcHRlZC4gT25seSB0aGUgZGFlbW9uIGl0c2VsZiBjaGFuZ2VkOmApO1xuICAgIGZvciAobGV0IHJlcG9VUkwgb2YgcmVwb1NvdXJjZXMpIHtcbiAgICAgICAgY29uc29sZS5sb2coYCAgJHtyZXBvVVJMfWApO1xuICAgIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gbGlzdFNvdXJjZXMoaG9zdDogc3RyaW5nKSB7XG4gICAgbGV0IHJlbW90ZUNvbmZpZyA9IGF3YWl0IHJlYWRSZW1vdGVDb25maWcoaG9zdCk7XG4gICAgaWYgKCFyZW1vdGVDb25maWcucmVwb1NvdXJjZXMubGVuZ3RoKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGAke2hvc3R9IGhhcyBubyBrZXkgc291cmNlcy4gcm9vdCdzIGF1dGhvcml6ZWRfa2V5cyBpcyBsZWZ0IGV4YWN0bHkgYXMgaXQgaXMuYCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc29sZS5sb2coYCR7aG9zdH0gbGV0cyByb290IGxvZyBpbiB3aXRoIHRoZSBrZXlzIGZyb20gJHtyZW1vdGVDb25maWcucmVwb1NvdXJjZXMubGVuZ3RofSByZXBvKHMpOmApO1xuICAgIGxldCBtZXJnZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBmb3IgKGxldCByZXBvVVJMIG9mIHJlbW90ZUNvbmZpZy5yZXBvU291cmNlcykge1xuICAgICAgICBsZXQga2V5cyA9IGF3YWl0IHJlYWRSZW1vdGVTb3VyY2VLZXlzKHsgaG9zdCwgcmVwb1VSTCB9KTtcbiAgICAgICAgY29uc29sZS5sb2coYFxcbiAgJHtyZXBvVVJMfWApO1xuICAgICAgICBpZiAoIWtleXMubGVuZ3RoKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgICAgIGdyYW50cyBubyBrZXlzIC0gdGhlIGNoZWNrb3V0IGlzIG1pc3Npbmcgb3IgZW1wdHlgKTtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUubG9nKGAgICAgZ3JhbnRzICR7a2V5cy5sZW5ndGh9IGtleShzKTpgKTtcbiAgICAgICAgZm9yIChsZXQga2V5IG9mIGtleXMpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGAgICAgICAke3N1bW1hcml6ZUtleShrZXkpfWApO1xuICAgICAgICAgICAgbWVyZ2VkLmFkZChrZXkpO1xuICAgICAgICB9XG4gICAgfVxuICAgIGlmIChyZW1vdGVDb25maWcucmVwb1NvdXJjZXMubGVuZ3RoID4gMSkge1xuICAgICAgICBjb25zb2xlLmxvZyhgXFxuJHttZXJnZWQuc2l6ZX0ga2V5KHMpIGluIHRvdGFsIG9uY2UgZHVwbGljYXRlcyBhcmUgbWVyZ2VkLmApO1xuICAgIH1cbn1cblxuLyoqIFRoZSB2ZXJiIGlzIGEgZml4ZWQgd29yZCByYXRoZXIgdGhhbiBhIHBvc2l0aW9uLCBzbyBpdCBpcyBwdWxsZWQgb3V0IG9mIHRoZSBhcmd1bWVudHMgd2hlcmV2ZXJcbiAgICBpdCB3YXMgdHlwZWQgYW5kIGV2ZXJ5dGhpbmcgbGVmdCBvdmVyIGlzIHBvc2l0aW9uYWwuICovXG5mdW5jdGlvbiBwYXJzZUFyZ3MoYXJndjogc3RyaW5nW10pIHtcbiAgICBsZXQgdmVyYnMgPSBhcmd2LmZpbHRlcihhcmcgPT4gVkVSQlMuaW5jbHVkZXMoYXJnKSk7XG4gICAgaWYgKCF2ZXJicy5sZW5ndGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBvbmUgb2YgJHtWRVJCUy5qb2luKFwiLCBcIil9IHNvbWV3aGVyZSBpbiB0aGUgYXJndW1lbnRzLCB3YXMgJHthcmd2LmpvaW4oXCIgXCIpIHx8IFwiKG5vdGhpbmcpXCJ9XFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgaWYgKHZlcmJzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBvbmUgb2YgJHtWRVJCUy5qb2luKFwiLCBcIil9LCB3YXMgJHt2ZXJicy5qb2luKFwiIGFuZCBcIil9XFxuJHtVU0FHRX1gKTtcbiAgICB9XG4gICAgbGV0IHZlcmIgPSB2ZXJic1swXTtcbiAgICBsZXQgW2hvc3QsIC4uLnJlc3RdID0gYXJndi5maWx0ZXIoYXJnID0+IGFyZyAhPT0gdmVyYik7XG4gICAgaWYgKCFob3N0KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSBob3N0LCB3YXMgbm90aGluZ1xcbiR7VVNBR0V9YCk7XG4gICAgfVxuICAgIHJldHVybiB7IHZlcmIsIGhvc3QsIHJlc3QgfTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1haW4oKSB7XG4gICAgbGV0IHsgdmVyYiwgaG9zdCwgcmVzdCB9ID0gcGFyc2VBcmdzKHByb2Nlc3MuYXJndi5zbGljZSgyKSk7XG5cbiAgICBpZiAodmVyYiA9PT0gXCJsaXN0XCIpIHtcbiAgICAgICAgYXdhaXQgbGlzdFNvdXJjZXMoaG9zdCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHZlcmIgPT09IFwidXBkYXRlXCIpIHtcbiAgICAgICAgaWYgKHJlc3QubGVuZ3RoKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIG5vdGhpbmcgYWZ0ZXIgdXBkYXRlLCB3YXMgJHtyZXN0Lmxlbmd0aH0gYXJndW1lbnQocylcXG4ke1VTQUdFfWApO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHVwZGF0ZURhZW1vbihob3N0KTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodmVyYiA9PT0gXCJhZGRcIikge1xuICAgICAgICBpZiAoIXJlc3QubGVuZ3RoIHx8IHJlc3QubGVuZ3RoID4gMikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBhIHByaXZhdGUga2V5IGFuZCBvcHRpb25hbGx5IGEgcmVwbyB1cmwsIHdhcyAke3Jlc3QubGVuZ3RofSBhcmd1bWVudChzKVxcbiR7VVNBR0V9YCk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgYWRkU291cmNlKHsgaG9zdCwga2V5UGF0aDogZXhwYW5kSG9tZShyZXN0WzBdKSwgcmVwb1VSTDogYXdhaXQgcmVzb2x2ZVJlcG9VUkwocmVzdFsxXSkgfSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHJlc3QubGVuZ3RoID4gMSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGF0IG1vc3QgYSByZXBvIHVybCB0byByZW1vdmUsIHdhcyAke3Jlc3QubGVuZ3RofSBhcmd1bWVudChzKVxcbiR7VVNBR0V9YCk7XG4gICAgfVxuICAgIGF3YWl0IHJlbW92ZVNvdXJjZSh7IGhvc3QsIHJlcG9VUkw6IGF3YWl0IHJlc29sdmVSZXBvVVJMKHJlc3RbMF0pIH0pO1xufVxuIl19
539
+ /* _JS_SOURCE_HASH = "3c90444025671cc3213dcfd869a36bc1c8cca2b502d5ce035d38986a40c7cf9f"; */