sliftutils 1.7.128 → 1.7.129

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 (51) hide show
  1. package/bin/addmachine.js +9 -0
  2. package/bin/machineclient.js +9 -0
  3. package/bin/machineserver.js +10 -0
  4. package/index.d.ts +15 -0
  5. package/misc/dist/ownIPs.ts.cache +43 -0
  6. package/misc/dist/strings.ts.cache +2 -2
  7. package/misc/https/dist/certs.ts.cache +2 -2
  8. package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
  9. package/misc/ownIPs.d.ts +11 -0
  10. package/misc/ownIPs.ts +34 -0
  11. package/package.json +9 -3
  12. package/security/authorizedKeys/authorizedKeys.ts +34 -0
  13. package/security/authorizedKeys/daemon/authLog.ts +12 -5
  14. package/security/authorizedKeys/daemon/changes.ts +16 -1
  15. package/security/authorizedKeys/daemon/daemon.ts +43 -34
  16. package/security/authorizedKeys/daemon/dist/changes.ts.cache +40 -0
  17. package/security/authorizedKeys/daemon/dist/git.ts.cache +121 -0
  18. package/security/authorizedKeys/daemon/dist/notify.ts.cache +39 -0
  19. package/security/authorizedKeys/daemon/dist/paths.ts.cache +29 -0
  20. package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +114 -0
  21. package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
  22. package/security/authorizedKeys/daemon/dist/sessions.ts.cache +147 -0
  23. package/security/authorizedKeys/daemon/dist/state.ts.cache +74 -0
  24. package/security/authorizedKeys/daemon/dist/trust.ts.cache +295 -0
  25. package/security/authorizedKeys/daemon/notify.ts +8 -2
  26. package/security/authorizedKeys/daemon/repoFiles.ts +118 -0
  27. package/security/authorizedKeys/daemon/revocation.ts +0 -0
  28. package/security/authorizedKeys/daemon/rootKeys.ts +77 -10
  29. package/security/authorizedKeys/daemon/state.ts +25 -7
  30. package/security/authorizedKeys/daemon/trust.ts +37 -16
  31. package/security/authorizedKeys/daemon/userKeys.ts +5 -4
  32. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +37 -3
  33. package/security/authorizedKeys/dist/revokeSource.ts.cache +17 -4
  34. package/security/authorizedKeys/dist/sources.ts.cache +52 -8
  35. package/security/authorizedKeys/dist/unrevoke.ts.cache +113 -17
  36. package/security/authorizedKeys/secureSSH.ts +39 -21
  37. package/security/authorizedKeys/unrevoke.ts +159 -8
  38. package/security/helpers/dist/remoteSSH.ts.cache +19 -9
  39. package/security/machines/addMachine.ts +83 -0
  40. package/security/machines/dist/addMachine.ts.cache +88 -0
  41. package/security/machines/dist/identity.ts.cache +176 -0
  42. package/security/machines/dist/machines.ts.cache +322 -0
  43. package/security/machines/dist/trustClient.ts.cache +48 -0
  44. package/security/machines/dist/trustState.ts.cache +57 -0
  45. package/security/machines/identity.ts +199 -0
  46. package/security/machines/machines.ts +355 -0
  47. package/security/machines/trustClient.ts +51 -0
  48. package/security/machines/trustServer.ts +85 -0
  49. package/security/machines/trustState.ts +53 -0
  50. package/security/notifications/discord.ts +14 -5
  51. package/security/notifications/dist/discord.ts.cache +17 -8
@@ -0,0 +1,322 @@
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.isMachineAccepted = exports.resolveKeysRepo = exports.machineState = exports.MACHINES_DIR = 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 paths_1 = require("../authorizedKeys/daemon/paths");
11
+ const repoFiles_1 = require("../authorizedKeys/daemon/repoFiles");
12
+ const repoFiles_2 = require("../authorizedKeys/daemon/repoFiles");
13
+ const revocation_1 = require("../authorizedKeys/daemon/revocation");
14
+ const git_1 = require("../authorizedKeys/daemon/git");
15
+ const repoFiles_3 = require("../authorizedKeys/daemon/repoFiles");
16
+ const trust_1 = require("../authorizedKeys/daemon/trust");
17
+ const revokeSource_1 = require("../authorizedKeys/revokeSource");
18
+ const sources_1 = require("../authorizedKeys/sources");
19
+ const notify_1 = require("../authorizedKeys/daemon/notify");
20
+ const discord_1 = require("../notifications/discord");
21
+ const trustState_1 = require("./trustState");
22
+ const spawn_1 = require("../helpers/spawn");
23
+ // Which machines this system talks to, kept in the same repo as the ssh keys. That repo is already
24
+ // signed, already distributed to every machine, and already has somewhere to record a rejection,
25
+ // so a second one would only be a second thing to keep in step.
26
+ exports.MACHINES_DIR = "machines";
27
+ const REVOCATIONS_DIR = "revocations";
28
+ const REVOCATION_REASON = "a machine talked to us from an unapproved IP";
29
+ // The revoke repo is pulled at most this often. A check happens per request, and a request must
30
+ // not cost a round trip to github.
31
+ const REVOKE_SYNC_INTERVAL = 60 * 1000;
32
+ // Where a Windows machine is expected to keep the repo, relative to the working directory. There
33
+ // is no daemon there to ask, and no /etc to look in.
34
+ const WINDOWS_REPO_PATH = "../authorized_keys";
35
+ async function pathExists(filePath) {
36
+ try {
37
+ await promises_1.default.access(filePath);
38
+ return true;
39
+ }
40
+ catch (e) {
41
+ return false;
42
+ }
43
+ }
44
+ function machineFilePath(repoPath, machineId) {
45
+ return path_1.default.join(repoPath, exports.MACHINES_DIR, `${machineId}.json`);
46
+ }
47
+ /** Reads every machine a checkout lists, or sets them.
48
+
49
+ Passing `machines` makes the repo match it exactly: machines named are written with the
50
+ addresses given, and machines not named are removed. That is why the addresses are part of
51
+ setting rather than a separate step - a machine with no address it may talk from is not a
52
+ machine we would accept anyway, so there is no state where naming one without them means
53
+ anything. Read first, change what you want, write the result.
54
+
55
+ Nothing here signs or commits anything. The signature is what every other machine checks before
56
+ believing any of this, and it takes the hardware key, so `yarn signfiles git` is still yours to
57
+ run afterwards. */
58
+ async function machineState(config) {
59
+ var _a;
60
+ let { repoPath, machines } = config;
61
+ let existing = await readMachines(repoPath);
62
+ if (!machines) {
63
+ return [...existing.values()];
64
+ }
65
+ let directory = path_1.default.join(repoPath, exports.MACHINES_DIR);
66
+ let written = [];
67
+ for (let machine of machines) {
68
+ if (!machine.machineId) {
69
+ throw new Error(`Expected a machineId, was ${JSON.stringify(machine.machineId)}`);
70
+ }
71
+ if (!machine.ips.length) {
72
+ throw new Error(`Expected addresses for ${machine.machineId}, was none. A machine is trusted from`
73
+ + ` the addresses it may talk from, so leave it out of the list to remove it.`);
74
+ }
75
+ let ips = machine.ips.filter((ip, index) => ip && machine.ips.indexOf(ip) === index);
76
+ let state = {
77
+ machineId: machine.machineId,
78
+ // In the order given, without duplicates, so the file reads the way it was set.
79
+ ips,
80
+ addedAt: ((_a = existing.get(machine.machineId)) === null || _a === void 0 ? void 0 : _a.addedAt) || new Date().toISOString(),
81
+ };
82
+ await promises_1.default.mkdir(directory, { recursive: true });
83
+ await promises_1.default.writeFile(machineFilePath(repoPath, state.machineId), JSON.stringify(state, undefined, 4) + "\n");
84
+ written.push(state);
85
+ }
86
+ // Whatever the repo had and the caller did not name is no longer trusted.
87
+ for (let machineId of existing.keys()) {
88
+ if (!machines.some(machine => machine.machineId === machineId)) {
89
+ await promises_1.default.rm(machineFilePath(repoPath, machineId), { force: true });
90
+ }
91
+ }
92
+ return written;
93
+ }
94
+ exports.machineState = machineState;
95
+ /** Every machine the repo lists. */
96
+ async function readMachines(repoPath) {
97
+ let machines = new Map();
98
+ let directory = path_1.default.join(repoPath, exports.MACHINES_DIR);
99
+ let names = await promises_1.default.readdir(directory).catch(() => []);
100
+ for (let name of names.sort()) {
101
+ if (!name.endsWith(".json")) {
102
+ continue;
103
+ }
104
+ try {
105
+ let parsed = JSON.parse(await promises_1.default.readFile(path_1.default.join(directory, name), "utf8"));
106
+ let machineId = parsed.machineId || name.replace(/\.json$/, "");
107
+ machines.set(machineId, { machineId, ips: parsed.ips || [], addedAt: parsed.addedAt || "" });
108
+ }
109
+ catch (e) {
110
+ console.log(`Ignoring unreadable machine file ${name}. ${e}`);
111
+ }
112
+ }
113
+ return machines;
114
+ }
115
+ async function isKeysRepo(repoPath) {
116
+ return await pathExists(path_1.default.join(repoPath, ".git"))
117
+ && await pathExists(path_1.default.join(repoPath, "authorized_keys"));
118
+ }
119
+ async function originOf(repoPath) {
120
+ let origin = await (0, spawn_1.spawnPromise)({ command: "git", args: ["remote", "get-url", "origin"], cwd: repoPath });
121
+ let url = origin.stdout.trim();
122
+ if (origin.status !== 0 || !url) {
123
+ throw new Error(`Expected ${repoPath} to have an origin remote, it has none`);
124
+ }
125
+ return url;
126
+ }
127
+ /** The keys repo this machine answers from, and the one anything editing the machine list edits.
128
+
129
+ On a host, that is the checkout the daemon already keeps up to date. On Windows there is no
130
+ daemon and no /etc, so the repo is expected beside the working directory, which is where a
131
+ developer working on both would have it.
132
+
133
+ Never the directory the command happens to be run from. Which repo this machine trusts is a
134
+ property of the machine, not of where somebody was standing when they typed something. */
135
+ async function resolveKeysRepo() {
136
+ if (os_1.default.platform() === "win32") {
137
+ let repoPath = path_1.default.resolve(WINDOWS_REPO_PATH);
138
+ if (!await isKeysRepo(repoPath)) {
139
+ throw new Error(`Expected ${repoPath} to be an authorized_keys repo, it is not.\n`
140
+ + `On Windows the repo is read from there, so clone it beside this one:\n`
141
+ + ` git clone <your authorized_keys repo> ${repoPath}`);
142
+ }
143
+ return { repoPath, sourceURL: await originOf(repoPath) };
144
+ }
145
+ let config = await promises_1.default.readFile(paths_1.CONFIG_PATH, "utf8").catch(() => "");
146
+ let sourceURL = config && (JSON.parse(config).repoSources || [])[0] || "";
147
+ if (!sourceURL) {
148
+ throw new Error(`Expected this machine to be set up with an authorized_keys repo, ${paths_1.CONFIG_PATH} names none.\n`
149
+ + `Set it up first:\n`
150
+ + ` yarn setupnotify <discord-webhook-url>\n`
151
+ + ` yarn securessh add <repo-private-key> <repo-url>`);
152
+ }
153
+ let repoPath = (0, sources_1.sourceRepoPath)(sourceURL);
154
+ if (!await isKeysRepo(repoPath)) {
155
+ throw new Error(`Expected a checkout of ${sourceURL} at ${repoPath}, there is none.\n`
156
+ + `Run \`yarn securessh update\` to put it back.`);
157
+ }
158
+ return { repoPath, sourceURL };
159
+ }
160
+ exports.resolveKeysRepo = resolveKeysRepo;
161
+ let lastRevokeSync = 0;
162
+ /** Pulled at most once a REVOKE_SYNC_INTERVAL, because this is asked per request. A sync that
163
+ fails leaves the checkout we already have, which is the safe direction: revocations we know
164
+ about stay known. */
165
+ async function syncRevocations(sourceURL) {
166
+ if (Date.now() - lastRevokeSync < REVOKE_SYNC_INTERVAL) {
167
+ return;
168
+ }
169
+ lastRevokeSync = Date.now();
170
+ try {
171
+ await (0, repoFiles_1.syncRepoFiles)((0, repoFiles_1.revokeRepo)(sourceURL));
172
+ }
173
+ catch (e) {
174
+ console.log(`Could not read ${(0, revokeSource_1.revokeRepoURL)(sourceURL)}, using the revocations already here. ${e}`);
175
+ }
176
+ }
177
+ /** Machine revocations, read out of the revoke repo the same way key revocations are. */
178
+ async function readMachineRevocations(sourceURL) {
179
+ let repo = (0, repoFiles_1.revokeRepo)(sourceURL);
180
+ let revocations = [];
181
+ for (let name of await (0, repoFiles_2.listRepoDir)(repo, REVOCATIONS_DIR)) {
182
+ if (!name.endsWith(".json")) {
183
+ continue;
184
+ }
185
+ try {
186
+ let parsed = JSON.parse(await (0, repoFiles_2.readRepoFile)(repo, path_1.default.join(REVOCATIONS_DIR, name)) || "");
187
+ if (parsed.machineId) {
188
+ revocations.push({
189
+ revocationId: parsed.revocationId || name.replace(/\.json$/, ""),
190
+ machineId: parsed.machineId,
191
+ ip: parsed.ip || "",
192
+ });
193
+ }
194
+ }
195
+ catch (e) {
196
+ console.log(`Ignoring unreadable revocation ${name}. ${e}`);
197
+ }
198
+ }
199
+ return revocations;
200
+ }
201
+ /** Sends the one notification this file is allowed to send, when it can.
202
+
203
+ The daemon configures notifications on startup and would simply send. This can also run in some
204
+ other process, which has not, so the webhook is picked up here if it is readable. A machine
205
+ with no webhook set up still records the revocation - being unable to tell anyone is not a
206
+ reason to keep accepting a machine that is being misused. */
207
+ async function notifyBestEffort(headline, body) {
208
+ try {
209
+ if (!(0, discord_1.areDiscordNotificationsConfigured)()) {
210
+ // Read first: configureDiscordNotifications exits the process when the file is missing,
211
+ // which is right for the daemon at startup and wrong for a library call.
212
+ await promises_1.default.readFile(discord_1.DEFAULT_WEBHOOK_FILE_PATH, "utf8");
213
+ await (0, discord_1.configureDiscordNotifications)({ filePath: discord_1.DEFAULT_WEBHOOK_FILE_PATH });
214
+ }
215
+ await (0, notify_1.notify)(headline, body);
216
+ }
217
+ catch (e) {
218
+ console.log(`Could not send a notification about this, it is only in the log. ${e}`);
219
+ }
220
+ }
221
+ /** Records that a machine we accept talked to us from an address it is not allowed from. One per
222
+ machine and address, so a second address is a second revocation and an unrevoke of the first
223
+ does not cover it. */
224
+ async function recordMachineRevocation(config) {
225
+ let { sourceURL, machineId, ip, hostLabel } = config;
226
+ let repoPath = (0, revokeSource_1.revokeRepoPath)(sourceURL);
227
+ let keyPath = await (0, repoFiles_3.ensureRevokeKey)(sourceURL);
228
+ let revocationId = (0, revocation_1.newRevocationId)(machineId);
229
+ let directory = path_1.default.join(repoPath, REVOCATIONS_DIR);
230
+ await promises_1.default.mkdir(directory, { recursive: true });
231
+ await promises_1.default.writeFile(path_1.default.join(directory, `${revocationId}.json`), JSON.stringify({
232
+ revocationId,
233
+ machineId,
234
+ ip,
235
+ revokedAt: new Date().toISOString(),
236
+ revokedBy: hostLabel,
237
+ reason: REVOCATION_REASON,
238
+ }, undefined, 4) + "\n");
239
+ await (0, git_1.runGit)({ args: ["add", "-A"], cwd: repoPath, keyPath });
240
+ await (0, git_1.runGit)({
241
+ args: ["-c", "user.email=portsecure@localhost", "-c", "user.name=portsecure", "commit", "-m", `revoke ${revocationId}`],
242
+ cwd: repoPath, keyPath,
243
+ });
244
+ let push = await (0, git_1.runGit)({ args: ["push", "origin", "HEAD"], cwd: repoPath, keyPath, allowFailure: true });
245
+ if (push.status !== 0) {
246
+ // Another machine most likely recorded the same thing first, and the next read picks it up.
247
+ console.log(`Could not push the revocation of ${machineId} from ${ip}. ${(push.stdout + push.stderr).trim()}`);
248
+ return;
249
+ }
250
+ console.log(`Revoked ${machineId} from ${ip}, ${revocationId}`);
251
+ // Said by whoever wrote the revocation, once, the same as for an ssh key. Machines that only
252
+ // read it later say nothing, or one event would be reported by every machine that saw it.
253
+ await notifyBestEffort(`SUSPICIOUS IP ${ip} FROZE MACHINE ${machineId}`, `A machine we trust talked to us from ${ip}, which is not an address it is allowed to talk`
254
+ + ` from. It proved it holds that machine's key, so either someone else has a copy of it,`
255
+ + ` or that machine's address changed.`
256
+ + `\n\nIt is frozen everywhere now, and nothing accepts it.`
257
+ + `\n\nIf this was an attack, remove \`machines/${machineId}.json\` from \`${sourceURL}\` now.`
258
+ + `\nIf it was legitimate, run \`yarn unrevoke git\` in that repo. It allows ${ip} for that`
259
+ + ` machine, and takes an hour to reach every machine.`
260
+ + `\n\nmachine: \`${machineId}\``
261
+ + `\nfrozen by: \`${hostLabel}\``);
262
+ }
263
+ /** Whether we will talk to this machine, coming from this address.
264
+
265
+ Both halves are required and neither is a guess: the caller knows the machine id because the
266
+ connection proved it, and knows the address because the packets came from there. A machine we
267
+ do not list is simply not accepted. A machine we do list, arriving from an address it does not
268
+ have, is treated as the same kind of event as a stolen ssh key - it is revoked everywhere, and
269
+ stays revoked until an unrevoke allows that machine from that address.
270
+
271
+ Throws when the repo itself cannot be read or is not signed, rather than answering false: that
272
+ is a broken installation, not a rejected machine, and the two deserve different handling. */
273
+ async function isMachineAccepted(config) {
274
+ let { machineId, ip } = config;
275
+ if (!machineId || !ip) {
276
+ throw new Error(`Expected a machineId and an ip, was ${JSON.stringify(machineId)} and ${JSON.stringify(ip)}`);
277
+ }
278
+ let { repoPath, sourceURL } = await resolveKeysRepo();
279
+ // Unsigned, or signed over different contents, means the machine list is not evidence of
280
+ // anything. Same gate the ssh keys go through.
281
+ await (0, trust_1.verifyCheckout)(repoPath);
282
+ let machine = (await readMachines(repoPath)).get(machineId);
283
+ if (!machine) {
284
+ return false;
285
+ }
286
+ await syncRevocations(sourceURL);
287
+ let unrevokes = await (0, revocation_1.readUnrevokes)(sourceURL).catch(() => ({ pairs: new Map(), legacyIds: new Map() }));
288
+ // An unrevoke is only honoured once it has waited out its hour, exactly as an ssh key's is.
289
+ let allowedAgain = async (pair) => {
290
+ let unrevokeId = unrevokes.pairs.get(pair);
291
+ return !!unrevokeId && await (0, trustState_1.unrevokeInEffect)(unrevokeId);
292
+ };
293
+ let revocations = await readMachineRevocations(sourceURL);
294
+ // Any revocation nothing has undone keeps the machine out, from everywhere, the way a revoked
295
+ // ssh key is out everywhere rather than only from the address it was misused from.
296
+ let revoked = false;
297
+ for (let revocation of revocations) {
298
+ if (revocation.machineId !== machineId) {
299
+ continue;
300
+ }
301
+ if (!await allowedAgain((0, revocation_1.pairKey)({ fingerprint: revocation.machineId, ip: revocation.ip }))) {
302
+ revoked = true;
303
+ }
304
+ }
305
+ if (revoked) {
306
+ return false;
307
+ }
308
+ if (machine.ips.includes(ip)) {
309
+ return true;
310
+ }
311
+ // Listed, but talking to us from somewhere it should not be. Recorded once for this machine
312
+ // and address, so being talked to repeatedly does not write repeatedly.
313
+ let pair = (0, revocation_1.pairKey)({ fingerprint: machineId, ip });
314
+ let alreadyRecorded = revocations.some(revocation => (0, revocation_1.pairKey)({ fingerprint: revocation.machineId, ip: revocation.ip }) === pair);
315
+ if (!alreadyRecorded && !await allowedAgain(pair)) {
316
+ await recordMachineRevocation({ sourceURL, machineId, ip, hostLabel: os_1.default.hostname() });
317
+ }
318
+ return false;
319
+ }
320
+ exports.isMachineAccepted = isMachineAccepted;
321
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFjaGluZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJtYWNoaW5lcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSwyREFBNkI7QUFDN0IsNENBQW9CO0FBQ3BCLGdEQUF3QjtBQUN4QiwwREFBNkQ7QUFDN0Qsa0VBQStFO0FBQy9FLGtFQUErRTtBQUMvRSxvRUFBbUg7QUFDbkgsc0RBQXNEO0FBQ3RELGtFQUFxRTtBQUNyRSwwREFBZ0U7QUFDaEUsaUVBQStFO0FBQy9FLHVEQUEyRDtBQUMzRCw0REFBeUQ7QUFDekQsc0RBQXVJO0FBQ3ZJLDZDQUFnRDtBQUNoRCw0Q0FBZ0Q7QUFFaEQsbUdBQW1HO0FBQ25HLGlHQUFpRztBQUNqRyxnRUFBZ0U7QUFDbkQsUUFBQSxZQUFZLEdBQUcsVUFBVSxDQUFDO0FBQ3ZDLE1BQU0sZUFBZSxHQUFHLGFBQWEsQ0FBQztBQUN0QyxNQUFNLGlCQUFpQixHQUFHLDhDQUE4QyxDQUFDO0FBQ3pFLGdHQUFnRztBQUNoRyxtQ0FBbUM7QUFDbkMsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ3ZDLGlHQUFpRztBQUNqRyxxREFBcUQ7QUFDckQsTUFBTSxpQkFBaUIsR0FBRyxvQkFBb0IsQ0FBQztBQVMvQyxLQUFLLFVBQVUsVUFBVSxDQUFDLFFBQWdCO0lBQ3RDLElBQUksQ0FBQztRQUNELE1BQU0sa0JBQUUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDMUIsT0FBTyxJQUFJLENBQUM7SUFDaEIsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDVCxPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsZUFBZSxDQUFDLFFBQWdCLEVBQUUsU0FBaUI7SUFDeEQsT0FBTyxjQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxvQkFBWSxFQUFFLEdBQUcsU0FBUyxPQUFPLENBQUMsQ0FBQztBQUNsRSxDQUFDO0FBRUQ7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SUFFRCw0RkFBNEY7SUFDNUYsd0VBQXdFO0lBQ3hFLElBQUksSUFBSSxHQUFHLElBQUEsb0JBQU8sRUFBQyxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNuRCxJQUFJLGVBQWUsR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQ2hELElBQUEsb0JBQU8sRUFBQyxFQUFFLFdBQVcsRUFBRSxVQUFVLENBQUMsU0FBUyxFQUFFLEVBQUUsRUFBRSxVQUFVLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQztJQUNoRixJQUFJLENBQUMsZUFBZSxJQUFJLENBQUMsTUFBTSxZQUFZLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUNoRCxNQUFNLHVCQUF1QixDQUFDLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLFlBQUUsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUYsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2pCLENBQUM7QUFuREQsOENBbURDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZzIGZyb20gXCJmcy9wcm9taXNlc1wiO1xuaW1wb3J0IG9zIGZyb20gXCJvc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IENPTkZJR19QQVRIIH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9wYXRoc1wiO1xuaW1wb3J0IHsgcmV2b2tlUmVwbywgc3luY1JlcG9GaWxlcyB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vcmVwb0ZpbGVzXCI7XG5pbXBvcnQgeyBsaXN0UmVwb0RpciwgcmVhZFJlcG9GaWxlIH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9yZXBvRmlsZXNcIjtcbmltcG9ydCB7IG5ld1Jldm9jYXRpb25JZCwgcGFpcktleSwgcmVhZFJldm9jYXRpb25GaWxlcywgcmVhZFVucmV2b2tlcyB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vcmV2b2NhdGlvblwiO1xuaW1wb3J0IHsgcnVuR2l0IH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9naXRcIjtcbmltcG9ydCB7IGVuc3VyZVJldm9rZUtleSB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vcmVwb0ZpbGVzXCI7XG5pbXBvcnQgeyB2ZXJpZnlDaGVja291dCB9IGZyb20gXCIuLi9hdXRob3JpemVkS2V5cy9kYWVtb24vdHJ1c3RcIjtcbmltcG9ydCB7IHJldm9rZVJlcG9QYXRoLCByZXZva2VSZXBvVVJMIH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL3Jldm9rZVNvdXJjZVwiO1xuaW1wb3J0IHsgc291cmNlUmVwb1BhdGggfSBmcm9tIFwiLi4vYXV0aG9yaXplZEtleXMvc291cmNlc1wiO1xuaW1wb3J0IHsgbm90aWZ5IH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL2RhZW1vbi9ub3RpZnlcIjtcbmltcG9ydCB7IGFyZURpc2NvcmROb3RpZmljYXRpb25zQ29uZmlndXJlZCwgY29uZmlndXJlRGlzY29yZE5vdGlmaWNhdGlvbnMsIERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEggfSBmcm9tIFwiLi4vbm90aWZpY2F0aW9ucy9kaXNjb3JkXCI7XG5pbXBvcnQgeyB1bnJldm9rZUluRWZmZWN0IH0gZnJvbSBcIi4vdHJ1c3RTdGF0ZVwiO1xuaW1wb3J0IHsgc3Bhd25Qcm9taXNlIH0gZnJvbSBcIi4uL2hlbHBlcnMvc3Bhd25cIjtcblxuLy8gV2hpY2ggbWFjaGluZXMgdGhpcyBzeXN0ZW0gdGFsa3MgdG8sIGtlcHQgaW4gdGhlIHNhbWUgcmVwbyBhcyB0aGUgc3NoIGtleXMuIFRoYXQgcmVwbyBpcyBhbHJlYWR5XG4vLyBzaWduZWQsIGFscmVhZHkgZGlzdHJpYnV0ZWQgdG8gZXZlcnkgbWFjaGluZSwgYW5kIGFscmVhZHkgaGFzIHNvbWV3aGVyZSB0byByZWNvcmQgYSByZWplY3Rpb24sXG4vLyBzbyBhIHNlY29uZCBvbmUgd291bGQgb25seSBiZSBhIHNlY29uZCB0aGluZyB0byBrZWVwIGluIHN0ZXAuXG5leHBvcnQgY29uc3QgTUFDSElORVNfRElSID0gXCJtYWNoaW5lc1wiO1xuY29uc3QgUkVWT0NBVElPTlNfRElSID0gXCJyZXZvY2F0aW9uc1wiO1xuY29uc3QgUkVWT0NBVElPTl9SRUFTT04gPSBcImEgbWFjaGluZSB0YWxrZWQgdG8gdXMgZnJvbSBhbiB1bmFwcHJvdmVkIElQXCI7XG4vLyBUaGUgcmV2b2tlIHJlcG8gaXMgcHVsbGVkIGF0IG1vc3QgdGhpcyBvZnRlbi4gQSBjaGVjayBoYXBwZW5zIHBlciByZXF1ZXN0LCBhbmQgYSByZXF1ZXN0IG11c3Rcbi8vIG5vdCBjb3N0IGEgcm91bmQgdHJpcCB0byBnaXRodWIuXG5jb25zdCBSRVZPS0VfU1lOQ19JTlRFUlZBTCA9IDYwICogMTAwMDtcbi8vIFdoZXJlIGEgV2luZG93cyBtYWNoaW5lIGlzIGV4cGVjdGVkIHRvIGtlZXAgdGhlIHJlcG8sIHJlbGF0aXZlIHRvIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4gVGhlcmVcbi8vIGlzIG5vIGRhZW1vbiB0aGVyZSB0byBhc2ssIGFuZCBubyAvZXRjIHRvIGxvb2sgaW4uXG5jb25zdCBXSU5ET1dTX1JFUE9fUEFUSCA9IFwiLi4vYXV0aG9yaXplZF9rZXlzXCI7XG5cbi8qKiBPbmUgbWFjaGluZSB3ZSBhcmUgd2lsbGluZyB0byB0YWxrIHRvLCBhbmQgdGhlIGFkZHJlc3NlcyBpdCBtYXkgdGFsayB0byB1cyBmcm9tLiAqL1xuZXhwb3J0IHR5cGUgTWFjaGluZVN0YXRlID0ge1xuICAgIG1hY2hpbmVJZDogc3RyaW5nO1xuICAgIGlwczogc3RyaW5nW107XG4gICAgYWRkZWRBdDogc3RyaW5nO1xufTtcblxuYXN5bmMgZnVuY3Rpb24gcGF0aEV4aXN0cyhmaWxlUGF0aDogc3RyaW5nKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZnMuYWNjZXNzKGZpbGVQYXRoKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBtYWNoaW5lRmlsZVBhdGgocmVwb1BhdGg6IHN0cmluZywgbWFjaGluZUlkOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gcGF0aC5qb2luKHJlcG9QYXRoLCBNQUNISU5FU19ESVIsIGAke21hY2hpbmVJZH0uanNvbmApO1xufVxuXG4vKiogUmVhZHMgZXZlcnkgbWFjaGluZSBhIGNoZWNrb3V0IGxpc3RzLCBvciBzZXRzIHRoZW0uXG5cbiAgICBQYXNzaW5nIGBtYWNoaW5lc2AgbWFrZXMgdGhlIHJlcG8gbWF0Y2ggaXQgZXhhY3RseTogbWFjaGluZXMgbmFtZWQgYXJlIHdyaXR0ZW4gd2l0aCB0aGVcbiAgICBhZGRyZXNzZXMgZ2l2ZW4sIGFuZCBtYWNoaW5lcyBub3QgbmFtZWQgYXJlIHJlbW92ZWQuIFRoYXQgaXMgd2h5IHRoZSBhZGRyZXNzZXMgYXJlIHBhcnQgb2ZcbiAgICBzZXR0aW5nIHJhdGhlciB0aGFuIGEgc2VwYXJhdGUgc3RlcCAtIGEgbWFjaGluZSB3aXRoIG5vIGFkZHJlc3MgaXQgbWF5IHRhbGsgZnJvbSBpcyBub3QgYVxuICAgIG1hY2hpbmUgd2Ugd291bGQgYWNjZXB0IGFueXdheSwgc28gdGhlcmUgaXMgbm8gc3RhdGUgd2hlcmUgbmFtaW5nIG9uZSB3aXRob3V0IHRoZW0gbWVhbnNcbiAgICBhbnl0aGluZy4gUmVhZCBmaXJzdCwgY2hhbmdlIHdoYXQgeW91IHdhbnQsIHdyaXRlIHRoZSByZXN1bHQuXG5cbiAgICBOb3RoaW5nIGhlcmUgc2lnbnMgb3IgY29tbWl0cyBhbnl0aGluZy4gVGhlIHNpZ25hdHVyZSBpcyB3aGF0IGV2ZXJ5IG90aGVyIG1hY2hpbmUgY2hlY2tzIGJlZm9yZVxuICAgIGJlbGlldmluZyBhbnkgb2YgdGhpcywgYW5kIGl0IHRha2VzIHRoZSBoYXJkd2FyZSBrZXksIHNvIGB5YXJuIHNpZ25maWxlcyBnaXRgIGlzIHN0aWxsIHlvdXJzIHRvXG4gICAgcnVuIGFmdGVyd2FyZHMuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFjaGluZVN0YXRlKGNvbmZpZzoge1xuICAgIHJlcG9QYXRoOiBzdHJpbmc7XG4gICAgbWFjaGluZXM/OiB7IG1hY2hpbmVJZDogc3RyaW5nOyBpcHM6IHN0cmluZ1tdIH1bXTtcbn0pOiBQcm9taXNlPE1hY2hpbmVTdGF0ZVtdPiB7XG4gICAgbGV0IHsgcmVwb1BhdGgsIG1hY2hpbmVzIH0gPSBjb25maWc7XG4gICAgbGV0IGV4aXN0aW5nID0gYXdhaXQgcmVhZE1hY2hpbmVzKHJlcG9QYXRoKTtcbiAgICBpZiAoIW1hY2hpbmVzKSB7XG4gICAgICAgIHJldHVybiBbLi4uZXhpc3RpbmcudmFsdWVzKCldO1xuICAgIH1cblxuICAgIGxldCBkaXJlY3RvcnkgPSBwYXRoLmpvaW4ocmVwb1BhdGgsIE1BQ0hJTkVTX0RJUik7XG4gICAgbGV0IHdyaXR0ZW46IE1hY2hpbmVTdGF0ZVtdID0gW107XG4gICAgZm9yIChsZXQgbWFjaGluZSBvZiBtYWNoaW5lcykge1xuICAgICAgICBpZiAoIW1hY2hpbmUubWFjaGluZUlkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIGEgbWFjaGluZUlkLCB3YXMgJHtKU09OLnN0cmluZ2lmeShtYWNoaW5lLm1hY2hpbmVJZCl9YCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFtYWNoaW5lLmlwcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICBgRXhwZWN0ZWQgYWRkcmVzc2VzIGZvciAke21hY2hpbmUubWFjaGluZUlkfSwgd2FzIG5vbmUuIEEgbWFjaGluZSBpcyB0cnVzdGVkIGZyb21gXG4gICAgICAgICAgICAgICAgKyBgIHRoZSBhZGRyZXNzZXMgaXQgbWF5IHRhbGsgZnJvbSwgc28gbGVhdmUgaXQgb3V0IG9mIHRoZSBsaXN0IHRvIHJlbW92ZSBpdC5gXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGxldCBpcHMgPSBtYWNoaW5lLmlwcy5maWx0ZXIoKGlwLCBpbmRleCkgPT4gaXAgJiYgbWFjaGluZS5pcHMuaW5kZXhPZihpcCkgPT09IGluZGV4KTtcbiAgICAgICAgbGV0IHN0YXRlOiBNYWNoaW5lU3RhdGUgPSB7XG4gICAgICAgICAgICBtYWNoaW5lSWQ6IG1hY2hpbmUubWFjaGluZUlkLFxuICAgICAgICAgICAgLy8gSW4gdGhlIG9yZGVyIGdpdmVuLCB3aXRob3V0IGR1cGxpY2F0ZXMsIHNvIHRoZSBmaWxlIHJlYWRzIHRoZSB3YXkgaXQgd2FzIHNldC5cbiAgICAgICAgICAgIGlwcyxcbiAgICAgICAgICAgIGFkZGVkQXQ6IGV4aXN0aW5nLmdldChtYWNoaW5lLm1hY2hpbmVJZCk/LmFkZGVkQXQgfHwgbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICB9O1xuICAgICAgICBhd2FpdCBmcy5ta2RpcihkaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgICBhd2FpdCBmcy53cml0ZUZpbGUobWFjaGluZUZpbGVQYXRoKHJlcG9QYXRoLCBzdGF0ZS5tYWNoaW5lSWQpLCBKU09OLnN0cmluZ2lmeShzdGF0ZSwgdW5kZWZpbmVkLCA0KSArIFwiXFxuXCIpO1xuICAgICAgICB3cml0dGVuLnB1c2goc3RhdGUpO1xuICAgIH1cblxuICAgIC8vIFdoYXRldmVyIHRoZSByZXBvIGhhZCBhbmQgdGhlIGNhbGxlciBkaWQgbm90IG5hbWUgaXMgbm8gbG9uZ2VyIHRydXN0ZWQuXG4gICAgZm9yIChsZXQgbWFjaGluZUlkIG9mIGV4aXN0aW5nLmtleXMoKSkge1xuICAgICAgICBpZiAoIW1hY2hpbmVzLnNvbWUobWFjaGluZSA9PiBtYWNoaW5lLm1hY2hpbmVJZCA9PT0gbWFjaGluZUlkKSkge1xuICAgICAgICAgICAgYXdhaXQgZnMucm0obWFjaGluZUZpbGVQYXRoKHJlcG9QYXRoLCBtYWNoaW5lSWQpLCB7IGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiB3cml0dGVuO1xufVxuXG4vKiogRXZlcnkgbWFjaGluZSB0aGUgcmVwbyBsaXN0cy4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlYWRNYWNoaW5lcyhyZXBvUGF0aDogc3RyaW5nKSB7XG4gICAgbGV0IG1hY2hpbmVzID0gbmV3IE1hcDxzdHJpbmcsIE1hY2hpbmVTdGF0ZT4oKTtcbiAgICBsZXQgZGlyZWN0b3J5ID0gcGF0aC5qb2luKHJlcG9QYXRoLCBNQUNISU5FU19ESVIpO1xuICAgIGxldCBuYW1lcyA9IGF3YWl0IGZzLnJlYWRkaXIoZGlyZWN0b3J5KS5jYXRjaCgoKSA9PiBbXSBhcyBzdHJpbmdbXSk7XG4gICAgZm9yIChsZXQgbmFtZSBvZiBuYW1lcy5zb3J0KCkpIHtcbiAgICAgICAgaWYgKCFuYW1lLmVuZHNXaXRoKFwiLmpzb25cIikpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBsZXQgcGFyc2VkID0gSlNPTi5wYXJzZShhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBuYW1lKSwgXCJ1dGY4XCIpKTtcbiAgICAgICAgICAgIGxldCBtYWNoaW5lSWQgPSBwYXJzZWQubWFjaGluZUlkIHx8IG5hbWUucmVwbGFjZSgvXFwuanNvbiQvLCBcIlwiKTtcbiAgICAgICAgICAgIG1hY2hpbmVzLnNldChtYWNoaW5lSWQsIHsgbWFjaGluZUlkLCBpcHM6IHBhcnNlZC5pcHMgfHwgW10sIGFkZGVkQXQ6IHBhcnNlZC5hZGRlZEF0IHx8IFwiXCIgfSk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBJZ25vcmluZyB1bnJlYWRhYmxlIG1hY2hpbmUgZmlsZSAke25hbWV9LiAke2V9YCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1hY2hpbmVzO1xufVxuXG5hc3luYyBmdW5jdGlvbiBpc0tleXNSZXBvKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYXdhaXQgcGF0aEV4aXN0cyhwYXRoLmpvaW4ocmVwb1BhdGgsIFwiLmdpdFwiKSlcbiAgICAgICAgJiYgYXdhaXQgcGF0aEV4aXN0cyhwYXRoLmpvaW4ocmVwb1BhdGgsIFwiYXV0aG9yaXplZF9rZXlzXCIpKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gb3JpZ2luT2YocmVwb1BhdGg6IHN0cmluZykge1xuICAgIGxldCBvcmlnaW4gPSBhd2FpdCBzcGF3blByb21pc2UoeyBjb21tYW5kOiBcImdpdFwiLCBhcmdzOiBbXCJyZW1vdGVcIiwgXCJnZXQtdXJsXCIsIFwib3JpZ2luXCJdLCBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgIGxldCB1cmwgPSBvcmlnaW4uc3Rkb3V0LnRyaW0oKTtcbiAgICBpZiAob3JpZ2luLnN0YXR1cyAhPT0gMCB8fCAhdXJsKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtyZXBvUGF0aH0gdG8gaGF2ZSBhbiBvcmlnaW4gcmVtb3RlLCBpdCBoYXMgbm9uZWApO1xuICAgIH1cbiAgICByZXR1cm4gdXJsO1xufVxuXG4vKiogVGhlIGtleXMgcmVwbyB0aGlzIG1hY2hpbmUgYW5zd2VycyBmcm9tLCBhbmQgdGhlIG9uZSBhbnl0aGluZyBlZGl0aW5nIHRoZSBtYWNoaW5lIGxpc3QgZWRpdHMuXG5cbiAgICBPbiBhIGhvc3QsIHRoYXQgaXMgdGhlIGNoZWNrb3V0IHRoZSBkYWVtb24gYWxyZWFkeSBrZWVwcyB1cCB0byBkYXRlLiBPbiBXaW5kb3dzIHRoZXJlIGlzIG5vXG4gICAgZGFlbW9uIGFuZCBubyAvZXRjLCBzbyB0aGUgcmVwbyBpcyBleHBlY3RlZCBiZXNpZGUgdGhlIHdvcmtpbmcgZGlyZWN0b3J5LCB3aGljaCBpcyB3aGVyZSBhXG4gICAgZGV2ZWxvcGVyIHdvcmtpbmcgb24gYm90aCB3b3VsZCBoYXZlIGl0LlxuXG4gICAgTmV2ZXIgdGhlIGRpcmVjdG9yeSB0aGUgY29tbWFuZCBoYXBwZW5zIHRvIGJlIHJ1biBmcm9tLiBXaGljaCByZXBvIHRoaXMgbWFjaGluZSB0cnVzdHMgaXMgYVxuICAgIHByb3BlcnR5IG9mIHRoZSBtYWNoaW5lLCBub3Qgb2Ygd2hlcmUgc29tZWJvZHkgd2FzIHN0YW5kaW5nIHdoZW4gdGhleSB0eXBlZCBzb21ldGhpbmcuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZUtleXNSZXBvKCkge1xuICAgIGlmIChvcy5wbGF0Zm9ybSgpID09PSBcIndpbjMyXCIpIHtcbiAgICAgICAgbGV0IHJlcG9QYXRoID0gcGF0aC5yZXNvbHZlKFdJTkRPV1NfUkVQT19QQVRIKTtcbiAgICAgICAgaWYgKCFhd2FpdCBpc0tleXNSZXBvKHJlcG9QYXRoKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgIGBFeHBlY3RlZCAke3JlcG9QYXRofSB0byBiZSBhbiBhdXRob3JpemVkX2tleXMgcmVwbywgaXQgaXMgbm90LlxcbmBcbiAgICAgICAgICAgICAgICArIGBPbiBXaW5kb3dzIHRoZSByZXBvIGlzIHJlYWQgZnJvbSB0aGVyZSwgc28gY2xvbmUgaXQgYmVzaWRlIHRoaXMgb25lOlxcbmBcbiAgICAgICAgICAgICAgICArIGAgIGdpdCBjbG9uZSA8eW91ciBhdXRob3JpemVkX2tleXMgcmVwbz4gJHtyZXBvUGF0aH1gXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IHJlcG9QYXRoLCBzb3VyY2VVUkw6IGF3YWl0IG9yaWdpbk9mKHJlcG9QYXRoKSB9O1xuICAgIH1cblxuICAgIGxldCBjb25maWcgPSBhd2FpdCBmcy5yZWFkRmlsZShDT05GSUdfUEFUSCwgXCJ1dGY4XCIpLmNhdGNoKCgpID0+IFwiXCIpO1xuICAgIGxldCBzb3VyY2VVUkwgPSBjb25maWcgJiYgKEpTT04ucGFyc2UoY29uZmlnKS5yZXBvU291cmNlcyB8fCBbXSlbMF0gfHwgXCJcIjtcbiAgICBpZiAoIXNvdXJjZVVSTCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgdGhpcyBtYWNoaW5lIHRvIGJlIHNldCB1cCB3aXRoIGFuIGF1dGhvcml6ZWRfa2V5cyByZXBvLCAke0NPTkZJR19QQVRIfSBuYW1lcyBub25lLlxcbmBcbiAgICAgICAgICAgICsgYFNldCBpdCB1cCBmaXJzdDpcXG5gXG4gICAgICAgICAgICArIGAgIHlhcm4gc2V0dXBub3RpZnkgPGRpc2NvcmQtd2ViaG9vay11cmw+XFxuYFxuICAgICAgICAgICAgKyBgICB5YXJuIHNlY3VyZXNzaCBhZGQgPHJlcG8tcHJpdmF0ZS1rZXk+IDxyZXBvLXVybD5gXG4gICAgICAgICk7XG4gICAgfVxuICAgIGxldCByZXBvUGF0aCA9IHNvdXJjZVJlcG9QYXRoKHNvdXJjZVVSTCk7XG4gICAgaWYgKCFhd2FpdCBpc0tleXNSZXBvKHJlcG9QYXRoKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgYSBjaGVja291dCBvZiAke3NvdXJjZVVSTH0gYXQgJHtyZXBvUGF0aH0sIHRoZXJlIGlzIG5vbmUuXFxuYFxuICAgICAgICAgICAgKyBgUnVuIFxcYHlhcm4gc2VjdXJlc3NoIHVwZGF0ZVxcYCB0byBwdXQgaXQgYmFjay5gXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB7IHJlcG9QYXRoLCBzb3VyY2VVUkwgfTtcbn1cblxubGV0IGxhc3RSZXZva2VTeW5jID0gMDtcblxuLyoqIFB1bGxlZCBhdCBtb3N0IG9uY2UgYSBSRVZPS0VfU1lOQ19JTlRFUlZBTCwgYmVjYXVzZSB0aGlzIGlzIGFza2VkIHBlciByZXF1ZXN0LiBBIHN5bmMgdGhhdFxuICAgIGZhaWxzIGxlYXZlcyB0aGUgY2hlY2tvdXQgd2UgYWxyZWFkeSBoYXZlLCB3aGljaCBpcyB0aGUgc2FmZSBkaXJlY3Rpb246IHJldm9jYXRpb25zIHdlIGtub3dcbiAgICBhYm91dCBzdGF5IGtub3duLiAqL1xuYXN5bmMgZnVuY3Rpb24gc3luY1Jldm9jYXRpb25zKHNvdXJjZVVSTDogc3RyaW5nKSB7XG4gICAgaWYgKERhdGUubm93KCkgLSBsYXN0UmV2b2tlU3luYyA8IFJFVk9LRV9TWU5DX0lOVEVSVkFMKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGFzdFJldm9rZVN5bmMgPSBEYXRlLm5vdygpO1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHN5bmNSZXBvRmlsZXMocmV2b2tlUmVwbyhzb3VyY2VVUkwpKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBDb3VsZCBub3QgcmVhZCAke3Jldm9rZVJlcG9VUkwoc291cmNlVVJMKX0sIHVzaW5nIHRoZSByZXZvY2F0aW9ucyBhbHJlYWR5IGhlcmUuICR7ZX1gKTtcbiAgICB9XG59XG5cbi8qKiBNYWNoaW5lIHJldm9jYXRpb25zLCByZWFkIG91dCBvZiB0aGUgcmV2b2tlIHJlcG8gdGhlIHNhbWUgd2F5IGtleSByZXZvY2F0aW9ucyBhcmUuICovXG5hc3luYyBmdW5jdGlvbiByZWFkTWFjaGluZVJldm9jYXRpb25zKHNvdXJjZVVSTDogc3RyaW5nKSB7XG4gICAgbGV0IHJlcG8gPSByZXZva2VSZXBvKHNvdXJjZVVSTCk7XG4gICAgbGV0IHJldm9jYXRpb25zOiB7IHJldm9jYXRpb25JZDogc3RyaW5nOyBtYWNoaW5lSWQ6IHN0cmluZzsgaXA6IHN0cmluZyB9W10gPSBbXTtcbiAgICBmb3IgKGxldCBuYW1lIG9mIGF3YWl0IGxpc3RSZXBvRGlyKHJlcG8sIFJFVk9DQVRJT05TX0RJUikpIHtcbiAgICAgICAgaWYgKCFuYW1lLmVuZHNXaXRoKFwiLmpzb25cIikpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBsZXQgcGFyc2VkID0gSlNPTi5wYXJzZShhd2FpdCByZWFkUmVwb0ZpbGUocmVwbywgcGF0aC5qb2luKFJFVk9DQVRJT05TX0RJUiwgbmFtZSkpIHx8IFwiXCIpO1xuICAgICAgICAgICAgaWYgKHBhcnNlZC5tYWNoaW5lSWQpIHtcbiAgICAgICAgICAgICAgICByZXZvY2F0aW9ucy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgcmV2b2NhdGlvbklkOiBwYXJzZWQucmV2b2NhdGlvbklkIHx8IG5hbWUucmVwbGFjZSgvXFwuanNvbiQvLCBcIlwiKSxcbiAgICAgICAgICAgICAgICAgICAgbWFjaGluZUlkOiBwYXJzZWQubWFjaGluZUlkLFxuICAgICAgICAgICAgICAgICAgICBpcDogcGFyc2VkLmlwIHx8IFwiXCIsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBJZ25vcmluZyB1bnJlYWRhYmxlIHJldm9jYXRpb24gJHtuYW1lfS4gJHtlfWApO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXZvY2F0aW9ucztcbn1cblxuLyoqIFNlbmRzIHRoZSBvbmUgbm90aWZpY2F0aW9uIHRoaXMgZmlsZSBpcyBhbGxvd2VkIHRvIHNlbmQsIHdoZW4gaXQgY2FuLlxuXG4gICAgVGhlIGRhZW1vbiBjb25maWd1cmVzIG5vdGlmaWNhdGlvbnMgb24gc3RhcnR1cCBhbmQgd291bGQgc2ltcGx5IHNlbmQuIFRoaXMgY2FuIGFsc28gcnVuIGluIHNvbWVcbiAgICBvdGhlciBwcm9jZXNzLCB3aGljaCBoYXMgbm90LCBzbyB0aGUgd2ViaG9vayBpcyBwaWNrZWQgdXAgaGVyZSBpZiBpdCBpcyByZWFkYWJsZS4gQSBtYWNoaW5lXG4gICAgd2l0aCBubyB3ZWJob29rIHNldCB1cCBzdGlsbCByZWNvcmRzIHRoZSByZXZvY2F0aW9uIC0gYmVpbmcgdW5hYmxlIHRvIHRlbGwgYW55b25lIGlzIG5vdCBhXG4gICAgcmVhc29uIHRvIGtlZXAgYWNjZXB0aW5nIGEgbWFjaGluZSB0aGF0IGlzIGJlaW5nIG1pc3VzZWQuICovXG5hc3luYyBmdW5jdGlvbiBub3RpZnlCZXN0RWZmb3J0KGhlYWRsaW5lOiBzdHJpbmcsIGJvZHk6IHN0cmluZykge1xuICAgIHRyeSB7XG4gICAgICAgIGlmICghYXJlRGlzY29yZE5vdGlmaWNhdGlvbnNDb25maWd1cmVkKCkpIHtcbiAgICAgICAgICAgIC8vIFJlYWQgZmlyc3Q6IGNvbmZpZ3VyZURpc2NvcmROb3RpZmljYXRpb25zIGV4aXRzIHRoZSBwcm9jZXNzIHdoZW4gdGhlIGZpbGUgaXMgbWlzc2luZyxcbiAgICAgICAgICAgIC8vIHdoaWNoIGlzIHJpZ2h0IGZvciB0aGUgZGFlbW9uIGF0IHN0YXJ0dXAgYW5kIHdyb25nIGZvciBhIGxpYnJhcnkgY2FsbC5cbiAgICAgICAgICAgIGF3YWl0IGZzLnJlYWRGaWxlKERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEgsIFwidXRmOFwiKTtcbiAgICAgICAgICAgIGF3YWl0IGNvbmZpZ3VyZURpc2NvcmROb3RpZmljYXRpb25zKHsgZmlsZVBhdGg6IERFRkFVTFRfV0VCSE9PS19GSUxFX1BBVEggfSk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgbm90aWZ5KGhlYWRsaW5lLCBib2R5KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBDb3VsZCBub3Qgc2VuZCBhIG5vdGlmaWNhdGlvbiBhYm91dCB0aGlzLCBpdCBpcyBvbmx5IGluIHRoZSBsb2cuICR7ZX1gKTtcbiAgICB9XG59XG5cbi8qKiBSZWNvcmRzIHRoYXQgYSBtYWNoaW5lIHdlIGFjY2VwdCB0YWxrZWQgdG8gdXMgZnJvbSBhbiBhZGRyZXNzIGl0IGlzIG5vdCBhbGxvd2VkIGZyb20uIE9uZSBwZXJcbiAgICBtYWNoaW5lIGFuZCBhZGRyZXNzLCBzbyBhIHNlY29uZCBhZGRyZXNzIGlzIGEgc2Vjb25kIHJldm9jYXRpb24gYW5kIGFuIHVucmV2b2tlIG9mIHRoZSBmaXJzdFxuICAgIGRvZXMgbm90IGNvdmVyIGl0LiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVjb3JkTWFjaGluZVJldm9jYXRpb24oY29uZmlnOiB7XG4gICAgc291cmNlVVJMOiBzdHJpbmc7XG4gICAgbWFjaGluZUlkOiBzdHJpbmc7XG4gICAgaXA6IHN0cmluZztcbiAgICBob3N0TGFiZWw6IHN0cmluZztcbn0pIHtcbiAgICBsZXQgeyBzb3VyY2VVUkwsIG1hY2hpbmVJZCwgaXAsIGhvc3RMYWJlbCB9ID0gY29uZmlnO1xuICAgIGxldCByZXBvUGF0aCA9IHJldm9rZVJlcG9QYXRoKHNvdXJjZVVSTCk7XG4gICAgbGV0IGtleVBhdGggPSBhd2FpdCBlbnN1cmVSZXZva2VLZXkoc291cmNlVVJMKTtcbiAgICBsZXQgcmV2b2NhdGlvbklkID0gbmV3UmV2b2NhdGlvbklkKG1hY2hpbmVJZCk7XG4gICAgbGV0IGRpcmVjdG9yeSA9IHBhdGguam9pbihyZXBvUGF0aCwgUkVWT0NBVElPTlNfRElSKTtcbiAgICBhd2FpdCBmcy5ta2RpcihkaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZShwYXRoLmpvaW4oZGlyZWN0b3J5LCBgJHtyZXZvY2F0aW9uSWR9Lmpzb25gKSwgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICByZXZvY2F0aW9uSWQsXG4gICAgICAgIG1hY2hpbmVJZCxcbiAgICAgICAgaXAsXG4gICAgICAgIHJldm9rZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICByZXZva2VkQnk6IGhvc3RMYWJlbCxcbiAgICAgICAgcmVhc29uOiBSRVZPQ0FUSU9OX1JFQVNPTixcbiAgICB9LCB1bmRlZmluZWQsIDQpICsgXCJcXG5cIik7XG5cbiAgICBhd2FpdCBydW5HaXQoeyBhcmdzOiBbXCJhZGRcIiwgXCItQVwiXSwgY3dkOiByZXBvUGF0aCwga2V5UGF0aCB9KTtcbiAgICBhd2FpdCBydW5HaXQoe1xuICAgICAgICBhcmdzOiBbXCItY1wiLCBcInVzZXIuZW1haWw9cG9ydHNlY3VyZUBsb2NhbGhvc3RcIiwgXCItY1wiLCBcInVzZXIubmFtZT1wb3J0c2VjdXJlXCIsIFwiY29tbWl0XCIsIFwiLW1cIiwgYHJldm9rZSAke3Jldm9jYXRpb25JZH1gXSxcbiAgICAgICAgY3dkOiByZXBvUGF0aCwga2V5UGF0aCxcbiAgICB9KTtcbiAgICBsZXQgcHVzaCA9IGF3YWl0IHJ1bkdpdCh7IGFyZ3M6IFtcInB1c2hcIiwgXCJvcmlnaW5cIiwgXCJIRUFEXCJdLCBjd2Q6IHJlcG9QYXRoLCBrZXlQYXRoLCBhbGxvd0ZhaWx1cmU6IHRydWUgfSk7XG4gICAgaWYgKHB1c2guc3RhdHVzICE9PSAwKSB7XG4gICAgICAgIC8vIEFub3RoZXIgbWFjaGluZSBtb3N0IGxpa2VseSByZWNvcmRlZCB0aGUgc2FtZSB0aGluZyBmaXJzdCwgYW5kIHRoZSBuZXh0IHJlYWQgcGlja3MgaXQgdXAuXG4gICAgICAgIGNvbnNvbGUubG9nKGBDb3VsZCBub3QgcHVzaCB0aGUgcmV2b2NhdGlvbiBvZiAke21hY2hpbmVJZH0gZnJvbSAke2lwfS4gJHsocHVzaC5zdGRvdXQgKyBwdXNoLnN0ZGVycikudHJpbSgpfWApO1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnNvbGUubG9nKGBSZXZva2VkICR7bWFjaGluZUlkfSBmcm9tICR7aXB9LCAke3Jldm9jYXRpb25JZH1gKTtcblxuICAgIC8vIFNhaWQgYnkgd2hvZXZlciB3cm90ZSB0aGUgcmV2b2NhdGlvbiwgb25jZSwgdGhlIHNhbWUgYXMgZm9yIGFuIHNzaCBrZXkuIE1hY2hpbmVzIHRoYXQgb25seVxuICAgIC8vIHJlYWQgaXQgbGF0ZXIgc2F5IG5vdGhpbmcsIG9yIG9uZSBldmVudCB3b3VsZCBiZSByZXBvcnRlZCBieSBldmVyeSBtYWNoaW5lIHRoYXQgc2F3IGl0LlxuICAgIGF3YWl0IG5vdGlmeUJlc3RFZmZvcnQoXG4gICAgICAgIGBTVVNQSUNJT1VTIElQICR7aXB9IEZST1pFIE1BQ0hJTkUgJHttYWNoaW5lSWR9YCxcbiAgICAgICAgYEEgbWFjaGluZSB3ZSB0cnVzdCB0YWxrZWQgdG8gdXMgZnJvbSAke2lwfSwgd2hpY2ggaXMgbm90IGFuIGFkZHJlc3MgaXQgaXMgYWxsb3dlZCB0byB0YWxrYFxuICAgICAgICArIGAgZnJvbS4gSXQgcHJvdmVkIGl0IGhvbGRzIHRoYXQgbWFjaGluZSdzIGtleSwgc28gZWl0aGVyIHNvbWVvbmUgZWxzZSBoYXMgYSBjb3B5IG9mIGl0LGBcbiAgICAgICAgKyBgIG9yIHRoYXQgbWFjaGluZSdzIGFkZHJlc3MgY2hhbmdlZC5gXG4gICAgICAgICsgYFxcblxcbkl0IGlzIGZyb3plbiBldmVyeXdoZXJlIG5vdywgYW5kIG5vdGhpbmcgYWNjZXB0cyBpdC5gXG4gICAgICAgICsgYFxcblxcbklmIHRoaXMgd2FzIGFuIGF0dGFjaywgcmVtb3ZlIFxcYG1hY2hpbmVzLyR7bWFjaGluZUlkfS5qc29uXFxgIGZyb20gXFxgJHtzb3VyY2VVUkx9XFxgIG5vdy5gXG4gICAgICAgICsgYFxcbklmIGl0IHdhcyBsZWdpdGltYXRlLCBydW4gXFxgeWFybiB1bnJldm9rZSBnaXRcXGAgaW4gdGhhdCByZXBvLiBJdCBhbGxvd3MgJHtpcH0gZm9yIHRoYXRgXG4gICAgICAgICsgYCBtYWNoaW5lLCBhbmQgdGFrZXMgYW4gaG91ciB0byByZWFjaCBldmVyeSBtYWNoaW5lLmBcbiAgICAgICAgKyBgXFxuXFxubWFjaGluZTogXFxgJHttYWNoaW5lSWR9XFxgYFxuICAgICAgICArIGBcXG5mcm96ZW4gYnk6IFxcYCR7aG9zdExhYmVsfVxcYGBcbiAgICApO1xufVxuXG4vKiogV2hldGhlciB3ZSB3aWxsIHRhbGsgdG8gdGhpcyBtYWNoaW5lLCBjb21pbmcgZnJvbSB0aGlzIGFkZHJlc3MuXG5cbiAgICBCb3RoIGhhbHZlcyBhcmUgcmVxdWlyZWQgYW5kIG5laXRoZXIgaXMgYSBndWVzczogdGhlIGNhbGxlciBrbm93cyB0aGUgbWFjaGluZSBpZCBiZWNhdXNlIHRoZVxuICAgIGNvbm5lY3Rpb24gcHJvdmVkIGl0LCBhbmQga25vd3MgdGhlIGFkZHJlc3MgYmVjYXVzZSB0aGUgcGFja2V0cyBjYW1lIGZyb20gdGhlcmUuIEEgbWFjaGluZSB3ZVxuICAgIGRvIG5vdCBsaXN0IGlzIHNpbXBseSBub3QgYWNjZXB0ZWQuIEEgbWFjaGluZSB3ZSBkbyBsaXN0LCBhcnJpdmluZyBmcm9tIGFuIGFkZHJlc3MgaXQgZG9lcyBub3RcbiAgICBoYXZlLCBpcyB0cmVhdGVkIGFzIHRoZSBzYW1lIGtpbmQgb2YgZXZlbnQgYXMgYSBzdG9sZW4gc3NoIGtleSAtIGl0IGlzIHJldm9rZWQgZXZlcnl3aGVyZSwgYW5kXG4gICAgc3RheXMgcmV2b2tlZCB1bnRpbCBhbiB1bnJldm9rZSBhbGxvd3MgdGhhdCBtYWNoaW5lIGZyb20gdGhhdCBhZGRyZXNzLlxuXG4gICAgVGhyb3dzIHdoZW4gdGhlIHJlcG8gaXRzZWxmIGNhbm5vdCBiZSByZWFkIG9yIGlzIG5vdCBzaWduZWQsIHJhdGhlciB0aGFuIGFuc3dlcmluZyBmYWxzZTogdGhhdFxuICAgIGlzIGEgYnJva2VuIGluc3RhbGxhdGlvbiwgbm90IGEgcmVqZWN0ZWQgbWFjaGluZSwgYW5kIHRoZSB0d28gZGVzZXJ2ZSBkaWZmZXJlbnQgaGFuZGxpbmcuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaXNNYWNoaW5lQWNjZXB0ZWQoY29uZmlnOiB7IG1hY2hpbmVJZDogc3RyaW5nOyBpcDogc3RyaW5nIH0pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBsZXQgeyBtYWNoaW5lSWQsIGlwIH0gPSBjb25maWc7XG4gICAgaWYgKCFtYWNoaW5lSWQgfHwgIWlwKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYSBtYWNoaW5lSWQgYW5kIGFuIGlwLCB3YXMgJHtKU09OLnN0cmluZ2lmeShtYWNoaW5lSWQpfSBhbmQgJHtKU09OLnN0cmluZ2lmeShpcCl9YCk7XG4gICAgfVxuICAgIGxldCB7IHJlcG9QYXRoLCBzb3VyY2VVUkwgfSA9IGF3YWl0IHJlc29sdmVLZXlzUmVwbygpO1xuICAgIC8vIFVuc2lnbmVkLCBvciBzaWduZWQgb3ZlciBkaWZmZXJlbnQgY29udGVudHMsIG1lYW5zIHRoZSBtYWNoaW5lIGxpc3QgaXMgbm90IGV2aWRlbmNlIG9mXG4gICAgLy8gYW55dGhpbmcuIFNhbWUgZ2F0ZSB0aGUgc3NoIGtleXMgZ28gdGhyb3VnaC5cbiAgICBhd2FpdCB2ZXJpZnlDaGVja291dChyZXBvUGF0aCk7XG5cbiAgICBsZXQgbWFjaGluZSA9IChhd2FpdCByZWFkTWFjaGluZXMocmVwb1BhdGgpKS5nZXQobWFjaGluZUlkKTtcbiAgICBpZiAoIW1hY2hpbmUpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGF3YWl0IHN5bmNSZXZvY2F0aW9ucyhzb3VyY2VVUkwpO1xuICAgIGxldCB1bnJldm9rZXMgPSBhd2FpdCByZWFkVW5yZXZva2VzKHNvdXJjZVVSTCkuY2F0Y2goKCkgPT4gKHsgcGFpcnM6IG5ldyBNYXAoKSwgbGVnYWN5SWRzOiBuZXcgTWFwKCkgfSkpO1xuICAgIC8vIEFuIHVucmV2b2tlIGlzIG9ubHkgaG9ub3VyZWQgb25jZSBpdCBoYXMgd2FpdGVkIG91dCBpdHMgaG91ciwgZXhhY3RseSBhcyBhbiBzc2gga2V5J3MgaXMuXG4gICAgbGV0IGFsbG93ZWRBZ2FpbiA9IGFzeW5jIChwYWlyOiBzdHJpbmcpID0+IHtcbiAgICAgICAgbGV0IHVucmV2b2tlSWQgPSB1bnJldm9rZXMucGFpcnMuZ2V0KHBhaXIpO1xuICAgICAgICByZXR1cm4gISF1bnJldm9rZUlkICYmIGF3YWl0IHVucmV2b2tlSW5FZmZlY3QodW5yZXZva2VJZCk7XG4gICAgfTtcbiAgICBsZXQgcmV2b2NhdGlvbnMgPSBhd2FpdCByZWFkTWFjaGluZVJldm9jYXRpb25zKHNvdXJjZVVSTCk7XG4gICAgLy8gQW55IHJldm9jYXRpb24gbm90aGluZyBoYXMgdW5kb25lIGtlZXBzIHRoZSBtYWNoaW5lIG91dCwgZnJvbSBldmVyeXdoZXJlLCB0aGUgd2F5IGEgcmV2b2tlZFxuICAgIC8vIHNzaCBrZXkgaXMgb3V0IGV2ZXJ5d2hlcmUgcmF0aGVyIHRoYW4gb25seSBmcm9tIHRoZSBhZGRyZXNzIGl0IHdhcyBtaXN1c2VkIGZyb20uXG4gICAgbGV0IHJldm9rZWQgPSBmYWxzZTtcbiAgICBmb3IgKGxldCByZXZvY2F0aW9uIG9mIHJldm9jYXRpb25zKSB7XG4gICAgICAgIGlmIChyZXZvY2F0aW9uLm1hY2hpbmVJZCAhPT0gbWFjaGluZUlkKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWF3YWl0IGFsbG93ZWRBZ2FpbihwYWlyS2V5KHsgZmluZ2VycHJpbnQ6IHJldm9jYXRpb24ubWFjaGluZUlkLCBpcDogcmV2b2NhdGlvbi5pcCB9KSkpIHtcbiAgICAgICAgICAgIHJldm9rZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfVxuICAgIGlmIChyZXZva2VkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBpZiAobWFjaGluZS5pcHMuaW5jbHVkZXMoaXApKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIC8vIExpc3RlZCwgYnV0IHRhbGtpbmcgdG8gdXMgZnJvbSBzb21ld2hlcmUgaXQgc2hvdWxkIG5vdCBiZS4gUmVjb3JkZWQgb25jZSBmb3IgdGhpcyBtYWNoaW5lXG4gICAgLy8gYW5kIGFkZHJlc3MsIHNvIGJlaW5nIHRhbGtlZCB0byByZXBlYXRlZGx5IGRvZXMgbm90IHdyaXRlIHJlcGVhdGVkbHkuXG4gICAgbGV0IHBhaXIgPSBwYWlyS2V5KHsgZmluZ2VycHJpbnQ6IG1hY2hpbmVJZCwgaXAgfSk7XG4gICAgbGV0IGFscmVhZHlSZWNvcmRlZCA9IHJldm9jYXRpb25zLnNvbWUocmV2b2NhdGlvbiA9PlxuICAgICAgICBwYWlyS2V5KHsgZmluZ2VycHJpbnQ6IHJldm9jYXRpb24ubWFjaGluZUlkLCBpcDogcmV2b2NhdGlvbi5pcCB9KSA9PT0gcGFpcik7XG4gICAgaWYgKCFhbHJlYWR5UmVjb3JkZWQgJiYgIWF3YWl0IGFsbG93ZWRBZ2FpbihwYWlyKSkge1xuICAgICAgICBhd2FpdCByZWNvcmRNYWNoaW5lUmV2b2NhdGlvbih7IHNvdXJjZVVSTCwgbWFjaGluZUlkLCBpcCwgaG9zdExhYmVsOiBvcy5ob3N0bmFtZSgpIH0pO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG59XG4iXX0=
322
+ /* _JS_SOURCE_HASH = "42ec23e7ee1a7879769634b3b36b652f35e6140757988d443d15d7a3a5d4adec"; */
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
3
+ //exports.main = exports.askIfTrusted = void 0;
4
+ const identity_1 = require("./identity");
5
+ // The other half of the demonstration: asks a server whether it trusts this machine. The answer
6
+ // depends on the address this machine reaches the server from, which is why the client cannot
7
+ // work it out alone.
8
+ const TRUST_PATH = "/trusted";
9
+ const USAGE = `Usage: yarn machineclient <url> <domain>
10
+
11
+ Asks that server whether this machine is trusted, proving which machine it is with a signed packet.
12
+ The server decides using the address we reach it from, so the answer is about how we are talking to
13
+ it, not only about who we are.
14
+
15
+ The domain is required: this machine has a different identity under each one, and the answer is
16
+ about the identity we sign with.`;
17
+ /** Asks one server whether it trusts this machine, as it is under this domain. */
18
+ async function askIfTrusted(config) {
19
+ let { baseURL, domain } = config;
20
+ let packet = await (0, identity_1.signTrustPacket)(domain);
21
+ let url = `${baseURL.replace(/\/+$/, "")}${TRUST_PATH}`;
22
+ let response = await fetch(url, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/json" },
25
+ body: JSON.stringify(packet),
26
+ });
27
+ return await response.json();
28
+ }
29
+ exports.askIfTrusted = askIfTrusted;
30
+ async function main() {
31
+ let [baseURL, domain] = process.argv.slice(2);
32
+ if (!baseURL || !domain) {
33
+ throw new Error(USAGE);
34
+ }
35
+ let answer = await askIfTrusted({ baseURL, domain });
36
+ console.log(`${answer.trusted && "TRUSTED" || "NOT TRUSTED"} by ${baseURL}`);
37
+ console.log(` machine ${answer.machineId || "(not established)"}`);
38
+ console.log(` seen coming from ${answer.ip || "(unknown)"}`);
39
+ if (answer.reason) {
40
+ console.log(` ${answer.reason}`);
41
+ }
42
+ if (!answer.trusted) {
43
+ process.exitCode = 1;
44
+ }
45
+ }
46
+ exports.main = main;
47
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJ1c3RDbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0cnVzdENsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx5Q0FBNkM7QUFFN0MsZ0dBQWdHO0FBQ2hHLDhGQUE4RjtBQUM5RixxQkFBcUI7QUFDckIsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDO0FBQzlCLE1BQU0sS0FBSyxHQUFHOzs7Ozs7O2lDQU9tQixDQUFDO0FBU2xDLGtGQUFrRjtBQUMzRSxLQUFLLFVBQVUsWUFBWSxDQUFDLE1BQTJDO0lBQzFFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDO0lBQ2pDLElBQUksTUFBTSxHQUFHLE1BQU0sSUFBQSwwQkFBZSxFQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzNDLElBQUksR0FBRyxHQUFHLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEdBQUcsVUFBVSxFQUFFLENBQUM7SUFDeEQsSUFBSSxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsR0FBRyxFQUFFO1FBQzVCLE1BQU0sRUFBRSxNQUFNO1FBQ2QsT0FBTyxFQUFFLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFO1FBQy9DLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQztLQUMvQixDQUFDLENBQUM7SUFDSCxPQUFPLE1BQU0sUUFBUSxDQUFDLElBQUksRUFBaUIsQ0FBQztBQUNoRCxDQUFDO0FBVkQsb0NBVUM7QUFFTSxLQUFLLFVBQVUsSUFBSTtJQUN0QixJQUFJLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzlDLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFDRCxJQUFJLE1BQU0sR0FBRyxNQUFNLFlBQVksQ0FBQyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQ3JELE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsT0FBTyxJQUFJLFNBQVMsSUFBSSxhQUFhLE9BQU8sT0FBTyxFQUFFLENBQUMsQ0FBQztJQUM3RSxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsTUFBTSxDQUFDLFNBQVMsSUFBSSxtQkFBbUIsRUFBRSxDQUFDLENBQUM7SUFDcEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsTUFBTSxDQUFDLEVBQUUsSUFBSSxXQUFXLEVBQUUsQ0FBQyxDQUFDO0lBQzlELElBQUksTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ2hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUN0QyxDQUFDO0lBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNsQixPQUFPLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQztJQUN6QixDQUFDO0FBQ0wsQ0FBQztBQWZELG9CQWVDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgc2lnblRydXN0UGFja2V0IH0gZnJvbSBcIi4vaWRlbnRpdHlcIjtcblxuLy8gVGhlIG90aGVyIGhhbGYgb2YgdGhlIGRlbW9uc3RyYXRpb246IGFza3MgYSBzZXJ2ZXIgd2hldGhlciBpdCB0cnVzdHMgdGhpcyBtYWNoaW5lLiBUaGUgYW5zd2VyXG4vLyBkZXBlbmRzIG9uIHRoZSBhZGRyZXNzIHRoaXMgbWFjaGluZSByZWFjaGVzIHRoZSBzZXJ2ZXIgZnJvbSwgd2hpY2ggaXMgd2h5IHRoZSBjbGllbnQgY2Fubm90XG4vLyB3b3JrIGl0IG91dCBhbG9uZS5cbmNvbnN0IFRSVVNUX1BBVEggPSBcIi90cnVzdGVkXCI7XG5jb25zdCBVU0FHRSA9IGBVc2FnZTogeWFybiBtYWNoaW5lY2xpZW50IDx1cmw+IDxkb21haW4+XG5cbkFza3MgdGhhdCBzZXJ2ZXIgd2hldGhlciB0aGlzIG1hY2hpbmUgaXMgdHJ1c3RlZCwgcHJvdmluZyB3aGljaCBtYWNoaW5lIGl0IGlzIHdpdGggYSBzaWduZWQgcGFja2V0LlxuVGhlIHNlcnZlciBkZWNpZGVzIHVzaW5nIHRoZSBhZGRyZXNzIHdlIHJlYWNoIGl0IGZyb20sIHNvIHRoZSBhbnN3ZXIgaXMgYWJvdXQgaG93IHdlIGFyZSB0YWxraW5nIHRvXG5pdCwgbm90IG9ubHkgYWJvdXQgd2hvIHdlIGFyZS5cblxuVGhlIGRvbWFpbiBpcyByZXF1aXJlZDogdGhpcyBtYWNoaW5lIGhhcyBhIGRpZmZlcmVudCBpZGVudGl0eSB1bmRlciBlYWNoIG9uZSwgYW5kIHRoZSBhbnN3ZXIgaXNcbmFib3V0IHRoZSBpZGVudGl0eSB3ZSBzaWduIHdpdGguYDtcblxuZXhwb3J0IHR5cGUgVHJ1c3RBbnN3ZXIgPSB7XG4gICAgdHJ1c3RlZDogYm9vbGVhbjtcbiAgICBtYWNoaW5lSWQ/OiBzdHJpbmc7XG4gICAgaXA/OiBzdHJpbmc7XG4gICAgcmVhc29uPzogc3RyaW5nO1xufTtcblxuLyoqIEFza3Mgb25lIHNlcnZlciB3aGV0aGVyIGl0IHRydXN0cyB0aGlzIG1hY2hpbmUsIGFzIGl0IGlzIHVuZGVyIHRoaXMgZG9tYWluLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFza0lmVHJ1c3RlZChjb25maWc6IHsgYmFzZVVSTDogc3RyaW5nOyBkb21haW46IHN0cmluZyB9KTogUHJvbWlzZTxUcnVzdEFuc3dlcj4ge1xuICAgIGxldCB7IGJhc2VVUkwsIGRvbWFpbiB9ID0gY29uZmlnO1xuICAgIGxldCBwYWNrZXQgPSBhd2FpdCBzaWduVHJ1c3RQYWNrZXQoZG9tYWluKTtcbiAgICBsZXQgdXJsID0gYCR7YmFzZVVSTC5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpfSR7VFJVU1RfUEFUSH1gO1xuICAgIGxldCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHVybCwge1xuICAgICAgICBtZXRob2Q6IFwiUE9TVFwiLFxuICAgICAgICBoZWFkZXJzOiB7IFwiQ29udGVudC1UeXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiIH0sXG4gICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHBhY2tldCksXG4gICAgfSk7XG4gICAgcmV0dXJuIGF3YWl0IHJlc3BvbnNlLmpzb24oKSBhcyBUcnVzdEFuc3dlcjtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1haW4oKSB7XG4gICAgbGV0IFtiYXNlVVJMLCBkb21haW5dID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpO1xuICAgIGlmICghYmFzZVVSTCB8fCAhZG9tYWluKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihVU0FHRSk7XG4gICAgfVxuICAgIGxldCBhbnN3ZXIgPSBhd2FpdCBhc2tJZlRydXN0ZWQoeyBiYXNlVVJMLCBkb21haW4gfSk7XG4gICAgY29uc29sZS5sb2coYCR7YW5zd2VyLnRydXN0ZWQgJiYgXCJUUlVTVEVEXCIgfHwgXCJOT1QgVFJVU1RFRFwifSBieSAke2Jhc2VVUkx9YCk7XG4gICAgY29uc29sZS5sb2coYCAgbWFjaGluZSAke2Fuc3dlci5tYWNoaW5lSWQgfHwgXCIobm90IGVzdGFibGlzaGVkKVwifWApO1xuICAgIGNvbnNvbGUubG9nKGAgIHNlZW4gY29taW5nIGZyb20gJHthbnN3ZXIuaXAgfHwgXCIodW5rbm93bilcIn1gKTtcbiAgICBpZiAoYW5zd2VyLnJlYXNvbikge1xuICAgICAgICBjb25zb2xlLmxvZyhgICAke2Fuc3dlci5yZWFzb259YCk7XG4gICAgfVxuICAgIGlmICghYW5zd2VyLnRydXN0ZWQpIHtcbiAgICAgICAgcHJvY2Vzcy5leGl0Q29kZSA9IDE7XG4gICAgfVxufVxuIl19
48
+ /* _JS_SOURCE_HASH = "51f52913bf4c7fbbeb7e5b3c70a7bc032fcc718b1eba1d8ed233fe7fe01617d2"; */
@@ -0,0 +1,57 @@
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.unrevokeInEffect = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const paths_1 = require("../authorizedKeys/daemon/paths");
10
+ const sources_1 = require("../authorizedKeys/sources");
11
+ const discord_1 = require("../notifications/discord");
12
+ // When this machine first saw each unrevoke, by unrevoke id.
13
+ //
14
+ // The same rule the ssh keys get, for the same reason: an unrevoke waits an hour before it counts,
15
+ // so a signing key that was itself stolen cannot instantly undo the revocation that shut it out.
16
+ // Counted from when we first saw it, never from a time the file states - whoever wrote the file
17
+ // chooses that, and would choose an hour ago.
18
+ //
19
+ // On disk, because a check runs in whatever process happens to be asking. Measuring from process
20
+ // start would restart the hour on every request and the unrevoke would never arrive. Kept beside
21
+ // the repo keys rather than in the daemon's state, because this has to work on a machine with no
22
+ // daemon, which is most of them.
23
+ const FIRST_SEEN_FILE = "machine-unrevokes.json";
24
+ function firstSeenPath() {
25
+ return path_1.default.join((0, sources_1.keysDir)(), FIRST_SEEN_FILE);
26
+ }
27
+ async function readFirstSeen() {
28
+ try {
29
+ return JSON.parse(await promises_1.default.readFile(firstSeenPath(), "utf8"));
30
+ }
31
+ catch (e) {
32
+ // Never written, or unreadable. Either way nothing has been seen before now.
33
+ return {};
34
+ }
35
+ }
36
+ /** Whether an unrevoke has waited out its hour, recording the first sighting if this is one.
37
+
38
+ Returns false the first time, which is the point: seeing an unrevoke is not the same as
39
+ honouring it. */
40
+ async function unrevokeInEffect(unrevokeId) {
41
+ let firstSeen = await readFirstSeen();
42
+ let seenAt = firstSeen[unrevokeId];
43
+ if (!seenAt) {
44
+ seenAt = Date.now();
45
+ firstSeen[unrevokeId] = seenAt;
46
+ await promises_1.default.mkdir(path_1.default.dirname(firstSeenPath()), { recursive: true, mode: 0o700 });
47
+ await promises_1.default.writeFile(firstSeenPath(), JSON.stringify(firstSeen, undefined, 4) + "\n");
48
+ }
49
+ if (Date.now() - seenAt < paths_1.UNREVOKE_DELAY) {
50
+ console.log(`Holding the unrevoke ${unrevokeId} until ${(0, discord_1.messageTimestamp)(new Date(seenAt + paths_1.UNREVOKE_DELAY))}`);
51
+ return false;
52
+ }
53
+ return true;
54
+ }
55
+ exports.unrevokeInEffect = unrevokeInEffect;
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJ1c3RTdGF0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRydXN0U3RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUN4QiwwREFBZ0U7QUFDaEUsdURBQW9EO0FBQ3BELHNEQUE0RDtBQUU1RCw2REFBNkQ7QUFDN0QsRUFBRTtBQUNGLG1HQUFtRztBQUNuRyxpR0FBaUc7QUFDakcsZ0dBQWdHO0FBQ2hHLDhDQUE4QztBQUM5QyxFQUFFO0FBQ0YsaUdBQWlHO0FBQ2pHLGlHQUFpRztBQUNqRyxpR0FBaUc7QUFDakcsaUNBQWlDO0FBQ2pDLE1BQU0sZUFBZSxHQUFHLHdCQUF3QixDQUFDO0FBRWpELFNBQVMsYUFBYTtJQUNsQixPQUFPLGNBQUksQ0FBQyxJQUFJLENBQUMsSUFBQSxpQkFBTyxHQUFFLEVBQUUsZUFBZSxDQUFDLENBQUM7QUFDakQsQ0FBQztBQUVELEtBQUssVUFBVSxhQUFhO0lBQ3hCLElBQUksQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLGtCQUFFLENBQUMsUUFBUSxDQUFDLGFBQWEsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDbEUsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDVCw2RUFBNkU7UUFDN0UsT0FBTyxFQUFFLENBQUM7SUFDZCxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7b0JBR29CO0FBQ2IsS0FBSyxVQUFVLGdCQUFnQixDQUFDLFVBQWtCO0lBQ3JELElBQUksU0FBUyxHQUFHLE1BQU0sYUFBYSxFQUFFLENBQUM7SUFDdEMsSUFBSSxNQUFNLEdBQUcsU0FBUyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ25DLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNWLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDcEIsU0FBUyxDQUFDLFVBQVUsQ0FBQyxHQUFHLE1BQU0sQ0FBQztRQUMvQixNQUFNLGtCQUFFLENBQUMsS0FBSyxDQUFDLGNBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7UUFDaEYsTUFBTSxrQkFBRSxDQUFDLFNBQVMsQ0FBQyxhQUFhLEVBQUUsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFDeEYsQ0FBQztJQUNELElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLE1BQU0sR0FBRyxzQkFBYyxFQUFFLENBQUM7UUFDdkMsT0FBTyxDQUFDLEdBQUcsQ0FDUCx3QkFBd0IsVUFBVSxVQUFVLElBQUEsMEJBQWdCLEVBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLHNCQUFjLENBQUMsQ0FBQyxFQUFFLENBQ3BHLENBQUM7UUFDRixPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDaEIsQ0FBQztBQWhCRCw0Q0FnQkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZnMgZnJvbSBcImZzL3Byb21pc2VzXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuaW1wb3J0IHsgVU5SRVZPS0VfREVMQVkgfSBmcm9tIFwiLi4vYXV0aG9yaXplZEtleXMvZGFlbW9uL3BhdGhzXCI7XG5pbXBvcnQgeyBrZXlzRGlyIH0gZnJvbSBcIi4uL2F1dGhvcml6ZWRLZXlzL3NvdXJjZXNcIjtcbmltcG9ydCB7IG1lc3NhZ2VUaW1lc3RhbXAgfSBmcm9tIFwiLi4vbm90aWZpY2F0aW9ucy9kaXNjb3JkXCI7XG5cbi8vIFdoZW4gdGhpcyBtYWNoaW5lIGZpcnN0IHNhdyBlYWNoIHVucmV2b2tlLCBieSB1bnJldm9rZSBpZC5cbi8vXG4vLyBUaGUgc2FtZSBydWxlIHRoZSBzc2gga2V5cyBnZXQsIGZvciB0aGUgc2FtZSByZWFzb246IGFuIHVucmV2b2tlIHdhaXRzIGFuIGhvdXIgYmVmb3JlIGl0IGNvdW50cyxcbi8vIHNvIGEgc2lnbmluZyBrZXkgdGhhdCB3YXMgaXRzZWxmIHN0b2xlbiBjYW5ub3QgaW5zdGFudGx5IHVuZG8gdGhlIHJldm9jYXRpb24gdGhhdCBzaHV0IGl0IG91dC5cbi8vIENvdW50ZWQgZnJvbSB3aGVuIHdlIGZpcnN0IHNhdyBpdCwgbmV2ZXIgZnJvbSBhIHRpbWUgdGhlIGZpbGUgc3RhdGVzIC0gd2hvZXZlciB3cm90ZSB0aGUgZmlsZVxuLy8gY2hvb3NlcyB0aGF0LCBhbmQgd291bGQgY2hvb3NlIGFuIGhvdXIgYWdvLlxuLy9cbi8vIE9uIGRpc2ssIGJlY2F1c2UgYSBjaGVjayBydW5zIGluIHdoYXRldmVyIHByb2Nlc3MgaGFwcGVucyB0byBiZSBhc2tpbmcuIE1lYXN1cmluZyBmcm9tIHByb2Nlc3Ncbi8vIHN0YXJ0IHdvdWxkIHJlc3RhcnQgdGhlIGhvdXIgb24gZXZlcnkgcmVxdWVzdCBhbmQgdGhlIHVucmV2b2tlIHdvdWxkIG5ldmVyIGFycml2ZS4gS2VwdCBiZXNpZGVcbi8vIHRoZSByZXBvIGtleXMgcmF0aGVyIHRoYW4gaW4gdGhlIGRhZW1vbidzIHN0YXRlLCBiZWNhdXNlIHRoaXMgaGFzIHRvIHdvcmsgb24gYSBtYWNoaW5lIHdpdGggbm9cbi8vIGRhZW1vbiwgd2hpY2ggaXMgbW9zdCBvZiB0aGVtLlxuY29uc3QgRklSU1RfU0VFTl9GSUxFID0gXCJtYWNoaW5lLXVucmV2b2tlcy5qc29uXCI7XG5cbmZ1bmN0aW9uIGZpcnN0U2VlblBhdGgoKSB7XG4gICAgcmV0dXJuIHBhdGguam9pbihrZXlzRGlyKCksIEZJUlNUX1NFRU5fRklMRSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRGaXJzdFNlZW4oKTogUHJvbWlzZTx7IFt1bnJldm9rZUlkOiBzdHJpbmddOiBudW1iZXIgfT4ge1xuICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKGZpcnN0U2VlblBhdGgoKSwgXCJ1dGY4XCIpKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIE5ldmVyIHdyaXR0ZW4sIG9yIHVucmVhZGFibGUuIEVpdGhlciB3YXkgbm90aGluZyBoYXMgYmVlbiBzZWVuIGJlZm9yZSBub3cuXG4gICAgICAgIHJldHVybiB7fTtcbiAgICB9XG59XG5cbi8qKiBXaGV0aGVyIGFuIHVucmV2b2tlIGhhcyB3YWl0ZWQgb3V0IGl0cyBob3VyLCByZWNvcmRpbmcgdGhlIGZpcnN0IHNpZ2h0aW5nIGlmIHRoaXMgaXMgb25lLlxuXG4gICAgUmV0dXJucyBmYWxzZSB0aGUgZmlyc3QgdGltZSwgd2hpY2ggaXMgdGhlIHBvaW50OiBzZWVpbmcgYW4gdW5yZXZva2UgaXMgbm90IHRoZSBzYW1lIGFzXG4gICAgaG9ub3VyaW5nIGl0LiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVucmV2b2tlSW5FZmZlY3QodW5yZXZva2VJZDogc3RyaW5nKSB7XG4gICAgbGV0IGZpcnN0U2VlbiA9IGF3YWl0IHJlYWRGaXJzdFNlZW4oKTtcbiAgICBsZXQgc2VlbkF0ID0gZmlyc3RTZWVuW3VucmV2b2tlSWRdO1xuICAgIGlmICghc2VlbkF0KSB7XG4gICAgICAgIHNlZW5BdCA9IERhdGUubm93KCk7XG4gICAgICAgIGZpcnN0U2Vlblt1bnJldm9rZUlkXSA9IHNlZW5BdDtcbiAgICAgICAgYXdhaXQgZnMubWtkaXIocGF0aC5kaXJuYW1lKGZpcnN0U2VlblBhdGgoKSksIHsgcmVjdXJzaXZlOiB0cnVlLCBtb2RlOiAwbzcwMCB9KTtcbiAgICAgICAgYXdhaXQgZnMud3JpdGVGaWxlKGZpcnN0U2VlblBhdGgoKSwgSlNPTi5zdHJpbmdpZnkoZmlyc3RTZWVuLCB1bmRlZmluZWQsIDQpICsgXCJcXG5cIik7XG4gICAgfVxuICAgIGlmIChEYXRlLm5vdygpIC0gc2VlbkF0IDwgVU5SRVZPS0VfREVMQVkpIHtcbiAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBgSG9sZGluZyB0aGUgdW5yZXZva2UgJHt1bnJldm9rZUlkfSB1bnRpbCAke21lc3NhZ2VUaW1lc3RhbXAobmV3IERhdGUoc2VlbkF0ICsgVU5SRVZPS0VfREVMQVkpKX1gXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG59XG4iXX0=
57
+ /* _JS_SOURCE_HASH = "eaf8b0fd92f88a8682308f89a16f004ca1c28ff8bfcd90a56c734f63669c2aeb"; */